Spaces:
Sleeping
Sleeping
| """Styling constants for the digital twin Gradio app.""" | |
| GOLD = "#ecad0a" | |
| BLUE = "#209dd7" | |
| PURPLE = "#753991" | |
| EXAMPLES = [ | |
| "Tell more about Naim's background and experience.", | |
| "What kinds of projects is Naim working on now?", | |
| "What are Naim's strongest technical skills?", | |
| "How can I get in touch with Naim?", | |
| ] | |
| CSS = CSS = """ | |
| :root { | |
| --twin-gold: #ecad0a; | |
| --twin-blue: #209dd7; | |
| --twin-purple: #753991; | |
| --twin-bg: #0d0d10; | |
| --twin-surface: #16161b; | |
| --twin-surface-2: #1c1c22; | |
| --twin-border: #2a2a32; | |
| --twin-border-strong: #3a3a44; | |
| --twin-text: #ececef; | |
| --twin-muted: #8c8c95; | |
| } | |
| body:not(.dark) { | |
| --twin-bg: #f4f4f6; | |
| --twin-surface: #ffffff; | |
| --twin-surface-2: #ededf0; | |
| --twin-border: #dcdce2; | |
| --twin-border-strong: #b8b8c0; | |
| --twin-text: #1a1a20; | |
| --twin-muted: #6a6a72; | |
| } | |
| footer, .built-with, .show-api, .api-docs { display: none !important; } | |
| /* ========== BACKGROUND: image on html, fade overlay on body ========== */ | |
| /* 1. The image lives on <html> — the outermost layer */ | |
| html { | |
| background-color: var(--twin-bg) !important; | |
| background-image: url('https://res.cloudinary.com/dgo7trcso/image/upload/v1782599281/profile_trayq6.jpg') !important; | |
| background-position: right center !important; | |
| background-repeat: no-repeat !important; | |
| background-size: auto 80% !important; | |
| background-attachment: fixed !important; | |
| min-height: 100% !important; | |
| } | |
| /* 2. <body> is transparent so the image shows through, | |
| but carries the left-to-right fade so the chat UI stays readable */ | |
| body { | |
| background-color: transparent !important; | |
| background-image: linear-gradient( | |
| to right, | |
| var(--twin-bg) 0%, /* solid bg on the left (chat area) */ | |
| var(--twin-bg) 55%, /* stays solid until past the container */ | |
| rgba(13,13,16,0.55) 75%, /* soft transition */ | |
| transparent 100% /* fully shows the image on the far right */ | |
| ) !important; | |
| background-attachment: fixed !important; | |
| background-size: 100% 100% !important; | |
| min-height: 100vh !important; | |
| } | |
| /* Light mode: same gradient but using the light background colour */ | |
| body:not(.dark) { | |
| background-image: linear-gradient( | |
| to right, | |
| #f4f4f6 0%, | |
| #f4f4f6 55%, | |
| rgba(244,244,246,0.55) 75%, | |
| transparent 100% | |
| ) !important; | |
| } | |
| /* 3. gradio-app and container are fully transparent — don't override the image */ | |
| gradio-app { | |
| background: transparent !important; | |
| min-height: 100vh !important; | |
| } | |
| /* ===================================================================== */ | |
| /* ---------- Stable layout ---------- */ | |
| .gradio-container { | |
| background: transparent !important; | |
| color: var(--twin-text) !important; | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important; | |
| width: 100% !important; | |
| max-width: 880px !important; | |
| min-width: 0 !important; | |
| margin: 0 auto !important; | |
| padding: 32px 24px 48px !important; | |
| } | |
| .gradio-container .main, .gradio-container .contain, .gradio-container .wrap { | |
| width: 100% !important; | |
| max-width: 100% !important; | |
| min-width: 0 !important; | |
| } | |
| .gradio-container * { min-width: 0; } | |
| /* ---------- Title ---------- */ | |
| .gradio-container h1 { | |
| color: var(--twin-text) !important; | |
| font-size: 26px !important; | |
| font-weight: 700 !important; | |
| letter-spacing: -0.02em !important; | |
| border-left: 3px solid var(--twin-gold); | |
| padding-left: 12px !important; | |
| margin: 4px 0 8px !important; | |
| text-align: left !important; | |
| } | |
| /* ---------- Sharp corners on structural pieces ---------- */ | |
| .chatbot, .chatbot *, .block, .form, | |
| button, input, textarea, | |
| .examples button { | |
| border-radius: 0 !important; | |
| } | |
| /* ---------- Block surfaces ---------- */ | |
| .block, .form { background: transparent !important; box-shadow: none !important; } | |
| /* ---------- Hide the Chatbot label / header strip ---------- */ | |
| .chatbot > .block-label, | |
| .chatbot > label, | |
| .chatbot .label-wrap, | |
| .chatbot .block-label, | |
| .chatbot > .label-container { | |
| display: none !important; | |
| } | |
| /* ---------- Chatbot frame ---------- */ | |
| .chatbot, .chatbot.block { | |
| background: var(--twin-surface) !important; | |
| border: 1px solid var(--twin-border) !important; | |
| min-height: 460px !important; | |
| box-shadow: none !important; | |
| } | |
| .chatbot .placeholder, .chatbot .placeholder * { color: var(--twin-muted) !important; } | |
| /* ---------- Message rows: strip parent backgrounds ---------- */ | |
| .message-row, | |
| .message-row > div, | |
| .message-row .role, | |
| .message-wrap, .bubble-wrap { | |
| background: transparent !important; | |
| border: 0 !important; | |
| box-shadow: none !important; | |
| } | |
| /* ---------- Reset borders on every bubble variant first ---------- */ | |
| .message-row .message, | |
| .message-row .message-bubble, | |
| .message-row .bubble { | |
| border: 0 !important; | |
| box-shadow: none !important; | |
| padding: 6px 10px !important; | |
| } | |
| /* ---------- Bubble backgrounds (broad to cover Gradio variants) ---------- */ | |
| .message-row.user-row .message, | |
| .message-row.user-row .message-bubble, | |
| .message-row.user-row .bubble, | |
| .message-row[data-role="user"] .message, | |
| .message-row[data-role="user"] .message-bubble { | |
| background: var(--twin-blue) !important; | |
| color: #ffffff !important; | |
| } | |
| .message-row.bot-row .message, | |
| .message-row.bot-row .message-bubble, | |
| .message-row.bot-row .bubble, | |
| .message-row[data-role="assistant"] .message, | |
| .message-row[data-role="assistant"] .message-bubble { | |
| background: var(--twin-surface-2) !important; | |
| color: var(--twin-text) !important; | |
| } | |
| /* ---------- Purple stripe ---------- */ | |
| .message-row.bot-row .message, | |
| .message-row.bot-row .bubble, | |
| .message-row.bot-row .message-bubble, | |
| .message-row[data-role="assistant"] .message, | |
| .message-row[data-role="assistant"] .bubble, | |
| .message-row[data-role="assistant"] .message-bubble { | |
| border-left: 2px solid var(--twin-purple) !important; | |
| } | |
| .message-row.bot-row .message .message, | |
| .message-row.bot-row .message .bubble, | |
| .message-row.bot-row .message .message-bubble, | |
| .message-row.bot-row .bubble .message, | |
| .message-row.bot-row .bubble .bubble, | |
| .message-row.bot-row .bubble .message-bubble, | |
| .message-row.bot-row .message-bubble .message, | |
| .message-row.bot-row .message-bubble .bubble, | |
| .message-row.bot-row .message-bubble .message-bubble, | |
| .message-row[data-role="assistant"] .message .message, | |
| .message-row[data-role="assistant"] .message .bubble, | |
| .message-row[data-role="assistant"] .message .message-bubble, | |
| .message-row[data-role="assistant"] .bubble .message, | |
| .message-row[data-role="assistant"] .bubble .bubble, | |
| .message-row[data-role="assistant"] .bubble .message-bubble, | |
| .message-row[data-role="assistant"] .message-bubble .message, | |
| .message-row[data-role="assistant"] .message-bubble .bubble, | |
| .message-row[data-role="assistant"] .message-bubble .message-bubble { | |
| border-left: 0 !important; | |
| } | |
| /* ---------- Uniform font size in bubbles ---------- */ | |
| .message-row .message, | |
| .message-row .message-bubble, | |
| .message-row .bubble { | |
| font-size: 14px !important; | |
| line-height: 1.55 !important; | |
| } | |
| .message-row .message p, | |
| .message-row .message-bubble p, | |
| .message-row .bubble p, | |
| .message-row .prose p { | |
| font-size: 14px !important; | |
| line-height: 1.55 !important; | |
| margin: 0 0 8px !important; | |
| color: inherit !important; | |
| } | |
| .message-row .message p:last-child, | |
| .message-row .message-bubble p:last-child, | |
| .message-row .bubble p:last-child, | |
| .message-row .prose p:last-child { margin-bottom: 0 !important; } | |
| /* Strip stray internal borders/backgrounds from anything inside a bubble */ | |
| .message-row .message *, | |
| .message-row .message-bubble *, | |
| .message-row .bubble * { | |
| background: transparent !important; | |
| border-color: transparent !important; | |
| box-shadow: none !important; | |
| color: inherit !important; | |
| } | |
| .message-row .message a, | |
| .message-row .message-bubble a { | |
| color: var(--twin-gold) !important; | |
| text-decoration: underline; | |
| } | |
| /* ---------- Input row alignment ---------- */ | |
| .input-row, | |
| .gr-input-row, | |
| .chat-input-row, | |
| form[class*="input"] { align-items: stretch !important; } | |
| textarea, input[type="text"] { | |
| background: var(--twin-surface) !important; | |
| border: 1px solid var(--twin-border) !important; | |
| color: var(--twin-text) !important; | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important; | |
| font-size: 14px !important; | |
| padding: 12px 14px !important; | |
| line-height: 1.4 !important; | |
| min-height: 48px !important; | |
| } | |
| textarea:focus, input[type="text"]:focus { | |
| border-color: var(--twin-gold) !important; | |
| outline: none !important; | |
| box-shadow: 0 0 0 1px var(--twin-gold) !important; | |
| } | |
| textarea::placeholder, input::placeholder { color: var(--twin-muted) !important; } | |
| /* ---------- Buttons ---------- */ | |
| button { | |
| font-family: 'JetBrains Mono', 'SF Mono', Menlo, monospace !important; | |
| letter-spacing: 0.12em !important; | |
| text-transform: uppercase !important; | |
| font-size: 11px !important; | |
| font-weight: 600 !important; | |
| border: 1px solid var(--twin-border) !important; | |
| background: transparent !important; | |
| color: var(--twin-text) !important; | |
| padding: 0 16px !important; | |
| min-height: 48px !important; | |
| align-self: stretch !important; | |
| display: inline-flex !important; | |
| align-items: center !important; | |
| justify-content: center !important; | |
| cursor: pointer; | |
| transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease; | |
| } | |
| button:hover { border-color: var(--twin-gold) !important; color: var(--twin-gold) !important; } | |
| button.primary, | |
| button[variant="primary"], | |
| button.submit, | |
| button.submit-button, | |
| .submit-button, | |
| button.lg.primary { | |
| background: var(--twin-gold) !important; | |
| border: 1px solid var(--twin-gold) !important; | |
| color: #111111 !important; | |
| min-height: 48px !important; | |
| align-self: stretch !important; | |
| padding: 0 14px !important; | |
| display: inline-flex !important; | |
| align-items: center !important; | |
| justify-content: center !important; | |
| } | |
| button.primary:hover, | |
| button.submit:hover, | |
| .submit-button:hover, | |
| button.lg.primary:hover { | |
| background: #ffc320 !important; | |
| border-color: #ffc320 !important; | |
| color: #111111 !important; | |
| } | |
| /* ---------- Submit-button icon ---------- */ | |
| button.submit svg, | |
| button.submit-button svg, | |
| .submit-button svg, | |
| button.primary svg, | |
| button[variant="primary"] svg { | |
| width: 18px !important; | |
| height: 18px !important; | |
| margin: 0 auto !important; | |
| display: block !important; | |
| align-self: center !important; | |
| color: #111111 !important; | |
| fill: currentColor !important; | |
| stroke: currentColor !important; | |
| } | |
| /* ---------- Examples ---------- */ | |
| .examples, .examples-holder, [data-testid="examples"] { | |
| background: transparent !important; | |
| padding: 0 !important; | |
| margin-top: 14px !important; | |
| } | |
| .examples table, .examples-table { background: transparent !important; border: 0 !important; } | |
| .examples button, .example, .examples td button, [data-testid="examples"] button { | |
| background: var(--twin-surface) !important; | |
| border: 1px solid var(--twin-border) !important; | |
| color: var(--twin-text) !important; | |
| text-transform: none !important; | |
| letter-spacing: 0 !important; | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important; | |
| font-size: 13px !important; | |
| font-weight: 400 !important; | |
| padding: 10px 14px !important; | |
| text-align: left !important; | |
| min-height: 0 !important; | |
| align-self: auto !important; | |
| display: inline-block !important; | |
| } | |
| .examples button:hover, .example:hover, [data-testid="examples"] button:hover { | |
| border-color: var(--twin-blue) !important; | |
| color: var(--twin-blue) !important; | |
| background: var(--twin-surface) !important; | |
| } | |
| /* ---------- Icon buttons (clear, retry, copy) ---------- */ | |
| .icon-button, .chatbot .icon-button { | |
| color: var(--twin-muted) !important; | |
| background: transparent !important; | |
| border: 0 !important; | |
| min-height: 0 !important; | |
| align-self: auto !important; | |
| padding: 4px !important; | |
| display: inline-flex !important; | |
| align-items: center !important; | |
| justify-content: center !important; | |
| } | |
| .icon-button:hover, .chatbot .icon-button:hover { color: var(--twin-gold) !important; } | |
| /* ---------- Scrollbar ---------- */ | |
| ::-webkit-scrollbar { width: 10px; height: 10px; } | |
| ::-webkit-scrollbar-track { background: var(--twin-bg); } | |
| ::-webkit-scrollbar-thumb { background: var(--twin-border-strong); } | |
| ::-webkit-scrollbar-thumb:hover { background: var(--twin-purple); } | |
| /* ---------- Selection ---------- */ | |
| ::selection { background: var(--twin-gold); color: #111111; } | |
| /* ---------- Mobile: hide image, full-width solid bg ---------- */ | |
| @media (max-width: 640px) { | |
| html { | |
| background-image: none !important; | |
| } | |
| body { | |
| background-image: none !important; | |
| background-color: var(--twin-bg) !important; | |
| } | |
| .gradio-container { padding: 22px 14px 36px !important; } | |
| .gradio-container h1 { font-size: 22px !important; } | |
| } | |
| """ | |
| JS = """ | |
| () => { | |
| document.title = 'Naim\'s Digital Twin'; | |
| const focusInput = () => { | |
| const areas = document.querySelectorAll('textarea'); | |
| if (areas.length) areas[areas.length - 1].focus(); | |
| }; | |
| setTimeout(focusInput, 300); | |
| // Re-focus the message field whenever Gradio re-enables it | |
| // (i.e. after the assistant finishes responding). | |
| const watchTextarea = (area) => { | |
| if (area.dataset.twinWatched) return; | |
| area.dataset.twinWatched = '1'; | |
| let wasDisabled = area.disabled || area.readOnly; | |
| new MutationObserver(() => { | |
| const isDisabled = area.disabled || area.readOnly; | |
| if (wasDisabled && !isDisabled) area.focus(); | |
| wasDisabled = isDisabled; | |
| }).observe(area, { attributes: true, attributeFilter: ['disabled', 'readonly'] }); | |
| }; | |
| const scan = () => document.querySelectorAll('textarea').forEach(watchTextarea); | |
| setTimeout(scan, 500); | |
| new MutationObserver(scan).observe(document.body, { childList: true, subtree: true }); | |
| } | |
| """ |