Tavashi/SkinSense_AI
0
1import streamlit as st2import time3 4# ── Page config ────────────────────────────────────────────────5st.set_page_config(6 page_title="SkinSense AI",7 page_icon="🌸",8 layout="centered",9 initial_sidebar_state="collapsed"10)11 12# ── Global CSS ─────────────────────────────────────────────────13st.markdown("""14<style>15@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Sans:wght@300;400;500;600&display=swap');16 17/* ── Reset & base ── */18*, *::before, *::after { box-sizing: border-box; }19 20html, body, .stApp {21 background: #FDF4F5 !important;22 font-family: 'DM Sans', sans-serif;23}24 25/* Hide Streamlit chrome */26#MainMenu, footer, header { visibility: hidden; }27.block-container {28 padding: 2rem 1.5rem 4rem !important;29 max-width: 720px !important;30}31 32/* ── Hero ── */33.hero {34 text-align: center;35 padding: 3rem 1rem 2rem;36}37.hero-eyebrow {38 font-size: 0.72rem;39 font-weight: 600;40 letter-spacing: 0.18em;41 color: #C9748A;42 text-transform: uppercase;43 margin-bottom: 0.8rem;44}45.hero-title {46 font-family: 'DM Serif Display', serif;47 font-size: 3rem;48 line-height: 1.1;49 color: #2B1A20;50 margin: 0 0 1rem;51}52.hero-title em {53 font-style: italic;54 color: #C9748A;55}56.hero-sub {57 font-size: 1rem;58 color: #7A5563;59 font-weight: 300;60 max-width: 420px;61 margin: 0 auto;62 line-height: 1.6;63 text-align: center !important;64}65 66/* ── Divider ── */67.soft-divider {68 height: 1px;69 background: linear-gradient(to right, transparent, #F0C4CE, transparent);70 margin: 2rem 0;71}72 73/* ── Section label ── */74.section-label {75 font-size: 0.7rem;76 font-weight: 600;77 letter-spacing: 0.15em;78 text-transform: uppercase;79 color: #C9748A;80 margin-bottom: 1.2rem;81}82 83/* ── Pill selectors ── */84.pill-group {85 display: flex;86 flex-wrap: wrap;87 gap: 0.5rem;88 margin-bottom: 0.5rem;89}90.pill {91 display: inline-block;92 padding: 0.45rem 1rem;93 border-radius: 999px;94 font-size: 0.85rem;95 font-weight: 500;96 cursor: pointer;97 border: 1.5px solid #F0C4CE;98 background: #FFFFFF;99 color: #7A5563;100 transition: all 0.15s ease;101 user-select: none;102}103.pill:hover { border-color: #C9748A; color: #C9748A; }104.pill.active {105 background: #C9748A;106 border-color: #C9748A;107 color: #FFFFFF;108}109 110/* ── Question block ── */111 112/* ── Generate button ── */113.stButton > button {114 width: 100%;115 background: linear-gradient(135deg, #C9748A 0%, #E8A0B0 100%) !important;116 color: white !important;117 border: none !important;118 border-radius: 14px !important;119 padding: 0.85rem 2rem !important;120 font-family: 'DM Sans', sans-serif !important;121 font-size: 1rem !important;122 font-weight: 600 !important;123 letter-spacing: 0.03em !important;124 cursor: pointer !important;125 margin-top: 1rem !important;126 transition: opacity 0.2s !important;127}128.stButton > button:hover { opacity: 0.88 !important; }129 130/* ── Routine cards ── */131.routine-card {132 background: #FFFFFF;133 border: 1px solid #F5DDE2;134 border-radius: 18px;135 padding: 1.4rem 1.6rem;136 margin-bottom: 0.85rem;137 display: flex;138 gap: 1.1rem;139 align-items: flex-start;140}141.card-step {142 min-width: 36px;143 height: 36px;144 border-radius: 50%;145 background: linear-gradient(135deg, #C9748A, #E8A0B0);146 color: white;147 font-size: 0.8rem;148 font-weight: 700;149 display: flex;150 align-items: center;151 justify-content: center;152 flex-shrink: 0;153 margin-top: 2px;154}155.card-body {}156.card-type {157 font-size: 0.7rem;158 font-weight: 600;159 letter-spacing: 0.12em;160 text-transform: uppercase;161 color: #C9748A;162 margin-bottom: 0.2rem;163}164.card-product {165 font-family: 'DM Serif Display', serif;166 font-size: 1.15rem;167 color: #2B1A20;168 margin-bottom: 0.3rem;169 line-height: 1.25;170}171.card-why {172 font-size: 0.82rem;173 color: #7A5563;174 line-height: 1.5;175 font-weight: 300;176}177.card-timing {178 display: inline-block;179 margin-top: 0.55rem;180 font-size: 0.7rem;181 font-weight: 600;182 letter-spacing: 0.08em;183 background: #FDF4F5;184 border: 1px solid #F0C4CE;185 color: #C9748A;186 border-radius: 999px;187 padding: 0.2rem 0.65rem;188}189 190/* ── Tips block ── */191.tips-block {192 background: linear-gradient(135deg, #FDF0F3, #FDE8EE);193 border: 1px solid #F0C4CE;194 border-radius: 16px;195 padding: 1.3rem 1.5rem;196 margin-top: 1.5rem;197}198.tips-title {199 font-size: 0.75rem;200 font-weight: 600;201 letter-spacing: 0.14em;202 text-transform: uppercase;203 color: #C9748A;204 margin-bottom: 0.8rem;205}206.tip-item {207 font-size: 0.85rem;208 color: #5A3540;209 padding: 0.35rem 0;210 border-bottom: 1px solid #F5DDE2;211 line-height: 1.5;212}213.tip-item:last-child { border-bottom: none; }214 215/* ── Streamlit widget overrides ── */216div[data-testid="stSelectbox"] > div > div {217 background: #FFFFFF !important;218 border: 1.5px solid #F0C4CE !important;219 border-radius: 12px !important;220 color: #2B1A20 !important;221 font-family: 'DM Sans', sans-serif !important;222}223div[data-testid="stMultiSelect"] > div > div {224 background: #FFFFFF !important;225 border: 1.5px solid #F0C4CE !important;226 border-radius: 12px !important;227}228div[data-testid="stMultiSelect"] span[data-baseweb="tag"] {229 background-color: #C9748A !important;230 border-radius: 999px !important;231}232label[data-testid="stWidgetLabel"] p {233 font-family: 'DM Sans', sans-serif !important;234 font-size: 0.95rem !important;235 font-weight: 600 !important;236 color: #2B1A20 !important;237}238</style>239""", unsafe_allow_html=True)240 241 242# ── Recommendation data ────────────────────────────────────────243 244CLEANSER_MAP = {245 "Very Dry": {246 "product": "Cream-to-Milk Gentle Cleanser",247 "ingredient": "Ceramide NP + Glycerin",248 "why": "Low-foam formula preserves the skin barrier — won't strip moisture that dry skin can't afford to lose.",249 "timing": "AM + PM"250 },251 "Dry": {252 "product": "Soothing Milky Cleanser",253 "ingredient": "Oat Extract + Allantoin",254 "why": "Allantoin calms dry, flaky skin while cleansing gently enough for twice-daily use.",255 "timing": "AM + PM"256 },257 "Balanced": {258 "product": "Gentle Daily Gel Wash",259 "ingredient": "Green Tea Extract + Panthenol",260 "why": "Mild surfactants clean without over-stripping — ideal for skin that doesn't need aggressive correction.",261 "timing": "AM + PM"262 },263 "Oily": {264 "product": "Pore-Clearing Foam Cleanser",265 "ingredient": "Salicylic Acid 0.5% + Zinc",266 "why": "BHA at low dose dissolves excess sebum inside pores without triggering rebound oiliness.",267 "timing": "AM + PM"268 },269 "Very Oily": {270 "product": "Deep-Clean Balancing Wash",271 "ingredient": "Salicylic Acid 2% + Niacinamide",272 "why": "2% BHA tackles congestion at the source. Niacinamide regulates sebum production over time.",273 "timing": "AM + PM"274 },275}276 277SERUM_MAP = {278 "Pimples": {279 "product": "Blemish Control Serum",280 "ingredient": "Niacinamide 10% + Zinc PCA",281 "why": "Niacinamide reduces inflammation and sebum production. Zinc PCA accelerates healing without dryness.",282 "timing": "PM"283 },284 "Cysts": {285 "product": "Retinol Renewal Serum",286 "ingredient": "Encapsulated Retinol 0.3%",287 "why": "Retinol speeds cell turnover to prevent pore blockages that cause deep cystic breakouts.",288 "timing": "PM only"289 },290 "Blackheads": {291 "product": "Pore-Refining Exfoliant",292 "ingredient": "BHA (Salicylic Acid 2%) + Witch Hazel",293 "why": "Oil-soluble BHA reaches inside pores to dislodge oxidized sebum — the root cause of blackheads.",294 "timing": "PM"295 },296 "Whiteheads": {297 "product": "Clarifying Treatment Serum",298 "ingredient": "Benzoyl Peroxide 2.5% + Allantoin",299 "why": "Low-dose BPO clears closed comedones without the peeling that higher concentrations cause.",300 "timing": "PM"301 },302 "Milia": {303 "product": "Gentle AHA Resurfacing Serum",304 "ingredient": "Glycolic Acid 5% + Lactic Acid 3%",305 "why": "Dual AHA blend softens keratin plugs that form milia. Start 2–3x weekly before daily use.",306 "timing": "PM"307 },308 "default": {309 "product": "Radiance Brightening Serum",310 "ingredient": "Vitamin C (THD Ascorbate) 10% + Ferulic Acid",311 "why": "Stable Vitamin C form boosts collagen and brightens evenly. Ferulic acid doubles its antioxidant life.",312 "timing": "AM"313 },314}315 316MOISTURIZER_MAP = {317 "Very Dry": {318 "product": "Barrier Repair Rich Cream",319 "ingredient": "Ceramide Complex + Shea Butter + Squalane",320 "why": "Triple lipid blend rebuilds the skin barrier and seals in moisture for 24-hour hydration.",321 "timing": "AM + PM"322 },323 "Dry": {324 "product": "Hydrating Day Cream",325 "ingredient": "Hyaluronic Acid (3 weights) + Glycerin",326 "why": "Multi-weight HA draws water into all skin layers. Glycerin holds it there all day.",327 "timing": "AM + PM"328 },329 "Balanced": {330 "product": "Lightweight Gel-Cream",331 "ingredient": "Aloe Vera + Beta-Glucan",332 "why": "Beta-glucan provides barrier support without heaviness. Perfect maintenance for skin that's already balanced.",333 "timing": "AM + PM"334 },335 "Oily": {336 "product": "Oil-Free Pore-Minimizing Gel",337 "ingredient": "Niacinamide 5% + Zinc + Dimethicone",338 "why": "Water-based texture hydrates without adding shine. Niacinamide tightens pores with consistent use.",339 "timing": "AM + PM"340 },341 "Very Oily": {342 "product": "Mattifying Water Gel",343 "ingredient": "Sebum-Control Complex + Kaolin",344 "why": "Kaolin absorbs excess oil throughout the day. Zero comedogenic ingredients — won't clog pores.",345 "timing": "AM + PM"346 },347}348 349SUNSCREEN_MAP = {350 "Minimal": {351 "product": "Everyday Tinted SPF 30",352 "ingredient": "Zinc Oxide + Iron Oxides",353 "why": "Mineral filter protects against UV and visible light. Tint evens skintone without foundation.",354 "timing": "AM only"355 },356 "Moderate": {357 "product": "Hydrating Broad-Spectrum SPF 50",358 "ingredient": "Tinosorb S + Uvinul A Plus + Hyaluronic Acid",359 "why": "Next-gen EU filters offer superior UVA protection vs. older US formulas. Added HA means no separate moisturizer needed.",360 "timing": "AM only — reapply every 2 hrs outdoors"361 },362 "High": {363 "product": "Sport Shield SPF 50+ PA++++",364 "ingredient": "Tinosorb M + Tinosorb S + Antioxidant Complex",365 "why": "PA++++ is the highest UVA rating. Antioxidants neutralize free radicals UV filters can't block.",366 "timing": "AM — reapply every 90 min"367 },368 "Very High": {369 "product": "Extreme Protection SPF 100+",370 "ingredient": "Mexoryl XL + Mexoryl SX + Zinc Oxide",371 "why": "Triple-filter system for maximum coverage. Required for prolonged direct sun — sports, beach, outdoor work.",372 "timing": "AM — reapply every hour"373 },374}375 376EXTRA_MAP = {377 "pore": {378 "product": "Pore-Refining Clay Mask",379 "ingredient": "Kaolin + Bentonite + Niacinamide",380 "why": "Use 2x weekly to deep-clean pores and tighten their appearance over time.",381 "timing": "2–3x weekly (PM)"382 },383 "acne": {384 "product": "Overnight Spot Patch Treatment",385 "ingredient": "Hydrocolloid + Tea Tree + Centella Asiatica",386 "why": "Hydrocolloid draws out impurities while you sleep. Centella calms redness by morning.",387 "timing": "PM (spot use)"388 },389 "default": {390 "product": "Brightening Eye Cream",391 "ingredient": "Caffeine + Peptides + Vitamin K",392 "why": "Caffeine drains puffiness. Peptides firm the delicate under-eye area with regular use.",393 "timing": "AM + PM"394 },395}396 397 398def build_routine(oiliness, concerns, pore, sun):399 steps = []400 401 # Step 1: Cleanser402 c = CLEANSER_MAP[oiliness]403 steps.append({"type": "Cleanser", **c})404 405 # Step 2: Serum — pick based on first concern, fallback to default406 if concerns:407 s = SERUM_MAP.get(concerns[0], SERUM_MAP["default"])408 else:409 s = SERUM_MAP["default"]410 steps.append({"type": "Treatment Serum", **s})411 412 # Step 3: Moisturizer413 m = MOISTURIZER_MAP[oiliness]414 steps.append({"type": "Moisturizer", **m})415 416 # Step 4: Sunscreen417 sp = SUNSCREEN_MAP[sun]418 steps.append({"type": "Sunscreen", **sp})419 420 # Step 5: Targeted extra421 if pore == "Large & Clogged":422 e = EXTRA_MAP["pore"]423 elif concerns and any(c in concerns for c in ["Pimples", "Cysts"]):424 e = EXTRA_MAP["acne"]425 else:426 e = EXTRA_MAP["default"]427 steps.append({"type": "Targeted Treatment", **e})428 429 return steps430 431 432def build_tips(oiliness, concerns, zones, sun):433 tips = []434 if oiliness in ["Oily", "Very Oily"]:435 tips.append("Use lukewarm — not hot — water. Heat triggers more sebum production.")436 if oiliness in ["Dry", "Very Dry"]:437 tips.append("Apply moisturizer within 60 seconds of washing while skin is still slightly damp.")438 if len(zones) >= 3:439 tips.append("Combination skin: use a richer moisturizer on dry zones, gel on oily zones.")440 if "Cysts" in concerns or "Pimples" in concerns:441 tips.append("Actives take 6–8 weeks to show results. Consistency beats switching products.")442 if sun in ["High", "Very High"]:443 tips.append("UV breaks down SPF. Reapply every 90–120 minutes when outdoors.")444 if not tips:445 tips.append("Patch test new actives on your jaw for 48 hours before applying to full face.")446 return tips447 448 449# ── App layout ─────────────────────────────────────────────────450 451# Hero452st.markdown("""453<div class="hero">454 <div class="hero-eyebrow">Skin Intelligence</div>455 <h1 class="hero-title"><em>Your skin,</em><br><em>finally understood.</em></h1>456 457</div>458""", unsafe_allow_html=True)459 460st.markdown("<div class='soft-divider'></div>", unsafe_allow_html=True)461 462# ── Inputs ─────────────────────────────────────────────────────463st.markdown("<div class='section-label'>Tell us about your skin</div>", unsafe_allow_html=True)464 465# Q1 — Oiliness466oiliness_options = ["Very Dry", "Dry", "Balanced", "Oily", "Very Oily"]467selected_oiliness = st.selectbox(468 "How does your skin feel by midday?",469 options=oiliness_options,470 index=2,471 help="Think about an average day without any products."472)473 474# Q2 — Zone Mapping475zone_options = ["T-zone", "Cheeks", "Forehead", "Chin"]476selected_zones = st.multiselect(477 "Which zones behave differently from the rest?",478 options=zone_options,479 default=[],480 help="Select all that apply. Leave blank if your skin is consistent."481)482 483# Q3 — Active Concerns484concern_options = ["Pimples", "Cysts", "Blackheads", "Whiteheads", "Milia"]485selected_concerns = st.multiselect(486 "Any active concerns right now?",487 options=concern_options,488 default=[],489 help="Select what's present on your skin currently."490)491 492# Q4 — Pore Appearance493pore_options = ["Invisible", "Noticeable", "Large & Clogged"]494selected_pore = st.selectbox(495 "How would you describe your pores?",496 options=pore_options,497 index=1498)499 500# Q5 — Sun Exposure501sun_options = ["Minimal", "Moderate", "High", "Very High"]502selected_sun = st.selectbox(503 "How much sun do you typically get?",504 options=sun_options,505 index=1,506 help="Minimal = mostly indoors. Very High = outdoors most of the day."507)508 509st.markdown("<div class='soft-divider'></div>", unsafe_allow_html=True)510 511# ── Generate button ─────────────────────────────────────────────512if st.button("Build My Routine ✦"):513 with st.spinner("Analysing your skin profile..."):514 time.sleep(1.4)515 516 routine = build_routine(selected_oiliness, selected_concerns, selected_pore, selected_sun)517 tips = build_tips(selected_oiliness, selected_concerns, selected_zones, selected_sun)518 519 st.markdown("<div class='section-label' style='margin-top:0.5rem;'>Your Personalized Routine</div>", unsafe_allow_html=True)520 521 for i, step in enumerate(routine, 1):522 st.markdown(f"""523 <div class="routine-card">524 <div class="card-step">{i:02d}</div>525 <div class="card-body">526 <div class="card-type">{step['type']}</div>527 <div class="card-product">{step['product']}</div>528 <div class="card-why">Key ingredient: <strong>{step['ingredient']}</strong><br>{step['why']}</div>529 <span class="card-timing">⏱ {step['timing']}</span>530 </div>531 </div>532 """, unsafe_allow_html=True)533 534 # Tips535 tips_html = "".join([f"<div class='tip-item'>→ {t}</div>" for t in tips])536 st.markdown(f"""537 <div class="tips-block">538 <div class="tips-title">💡 Quick Wins for Your Skin Type</div>539 {tips_html}540 </div>541 """, unsafe_allow_html=True)542 543 st.markdown("<div class='soft-divider'></div>", unsafe_allow_html=True)544 st.markdown("<p style='text-align:center; color:#B08090; font-size:0.8rem;'>Results are based on your inputs. Always patch-test new products.</p>", unsafe_allow_html=True)545 546# Footer547st.markdown("<p style='text-align:center; color:#D4A0B0; font-size:0.75rem; margin-top:3rem;'>SkinSense AI • Powered by Streamlit</p>", unsafe_allow_html=True)