PaperProf / ui /index.html
Ryadg's picture
feat: full-height libraries, pre-generated reward image, English upload text
ee14d3e
Raw
History Blame Contribute Delete
60.6 kB
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<style>
/* ── Reset & base ─────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #0A0F1E;
--surface: rgba(255,255,255,0.04);
--surface-2: rgba(255,255,255,0.07);
--border: rgba(255,255,255,0.08);
--border-glow: rgba(124,58,237,0.5);
--purple: #7C3AED;
--purple-l: #A78BFA;
--cyan: #06B6D4;
--cyan-l: #67E8F9;
--green: #10B981;
--amber: #F59E0B;
--red: #EF4444;
--text: #F1F5F9;
--muted: #94A3B8;
--dim: #475569;
--r: 18px;
--r-sm: 10px;
}
html { scroll-behavior: smooth; }
body {
font-family: 'Inter', system-ui, sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
overflow-x: hidden;
background-image:
radial-gradient(ellipse at 15% 40%, rgba(124,58,237,0.10) 0%, transparent 55%),
radial-gradient(ellipse at 85% 15%, rgba(6,182,212,0.08) 0%, transparent 55%),
radial-gradient(ellipse at 60% 90%, rgba(124,58,237,0.06) 0%, transparent 50%);
}
/* ── Particles canvas ─────────────────────────────────────────────────── */
#particles {
position: fixed; inset: 0;
pointer-events: none; z-index: 0;
}
/* ── Layout ───────────────────────────────────────────────────────────── */
#app {
position: relative; z-index: 1;
max-width: 760px;
margin: 0 auto;
padding: 28px 20px 80px;
display: flex; flex-direction: column; gap: 20px;
}
/* ── Header β€” amphitheater hero ───────────────────────────────────────── */
.hero {
position: relative;
padding: 12px 0 0;
animation: fadeUp .6s ease both;
}
.amphitheater {
position: relative; z-index: 1;
height: 380px;
perspective: 800px;
overflow: hidden;
border-radius: var(--r);
border: 1px solid var(--border);
background:
radial-gradient(ellipse 70% 45% at 50% -8%, rgba(167,139,250,.16) 0%, transparent 60%),
radial-gradient(ellipse 60% 35% at 50% 112%, rgba(6,182,212,.12) 0%, transparent 65%),
linear-gradient(180deg, #0B1126 0%, #0A0F1E 55%, #0C1226 100%);
box-shadow: 0 8px 32px rgba(0,0,0,.45), 0 1px 0 rgba(255,255,255,.04) inset;
}
/* ceiling lights */
.amphi-light {
position: absolute; z-index: 1;
width: 4px; height: 4px; border-radius: 50%;
background: #CFE9FF;
box-shadow: 0 0 8px 2px rgba(167,139,250,.55);
animation: twinkle 3.2s ease-in-out infinite;
}
.amphi-light:nth-of-type(2n) {
box-shadow: 0 0 8px 2px rgba(103,232,249,.5);
}
/* ambient beam from above */
.amphi-beam {
position: absolute; inset: 0; z-index: 1; pointer-events: none;
background: radial-gradient(ellipse 55% 50% at 50% 0%, rgba(241,245,249,.07), transparent 70%);
}
/* curved seat rows receding into the distance */
.amphi-rows {
position: absolute; inset: 0; z-index: 1;
transform-style: preserve-3d;
}
.amphi-row {
position: absolute; left: 50%;
transform: translateX(-50%) rotateX(48deg);
transform-origin: 50% 100%;
border-radius: 50% 50% 0 0 / 90% 90% 0 0;
background:
linear-gradient(180deg, rgba(255,255,255,.09) 0%, transparent 35%),
repeating-linear-gradient(90deg,
#41322a 0,
#41322a calc(var(--seat) - 6px),
#221a15 calc(var(--seat) - 6px),
#221a15 var(--seat));
box-shadow: 0 10px 18px rgba(0,0,0,.55);
}
.amphi-row:nth-child(1) { --seat: 18px; width: 54%; height: 16px; top: 30%; opacity: .45; }
.amphi-row:nth-child(2) { --seat: 21px; width: 66%; height: 20px; top: 41%; opacity: .6; }
.amphi-row:nth-child(3) { --seat: 24px; width: 80%; height: 24px; top: 52%; opacity: .75; }
.amphi-row:nth-child(4) { --seat: 27px; width: 94%; height: 28px; top: 64%; opacity: .88; }
.amphi-row:nth-child(5) { --seat: 30px; width: 110%; height: 32px; top: 77%; opacity: 1; }
/* glowing stage + podium */
.amphi-stage {
position: absolute; bottom: -8px; left: 50%; z-index: 2;
transform: translateX(-50%);
width: 60%; height: 74px; pointer-events: none;
background: radial-gradient(ellipse at 50% 100%, rgba(6,182,212,.32), rgba(124,58,237,.16) 45%, transparent 72%);
filter: blur(2px);
}
.amphi-podium {
position: absolute; bottom: 18px; left: 50%; z-index: 2;
transform: translateX(-50%);
width: 130px; height: 26px; pointer-events: none;
border-radius: 50%;
background: radial-gradient(ellipse, rgba(103,232,249,.45), rgba(124,58,237,.22) 60%, transparent 78%);
box-shadow: 0 0 30px rgba(6,182,212,.35);
}
/* ── AI professor character ───────────────────────────────────────────── */
.professor {
position: absolute; bottom: 36px; left: 50%; z-index: 3;
width: 92px; margin-left: -46px; /* centered without transform β€” float animation owns transform */
cursor: pointer;
animation: prof-float 3.6s ease-in-out infinite;
filter: drop-shadow(0 10px 14px rgba(0,0,0,.5));
}
.prof-antenna {
width: 3px; height: 14px; margin: 0 auto; position: relative;
background: linear-gradient(180deg, var(--cyan-l), var(--purple));
border-radius: 2px;
}
.prof-antenna::after {
content: ''; position: absolute; top: -7px; left: 50%;
transform: translateX(-50%);
width: 9px; height: 9px; border-radius: 50%;
background: var(--cyan-l);
box-shadow: 0 0 10px 2px rgba(103,232,249,.8);
animation: pulse 2.2s ease-in-out infinite;
}
.prof-head {
width: 66px; height: 54px; margin: 0 auto; position: relative;
display: flex; align-items: center; justify-content: center; gap: 10px;
border-radius: 18px;
background: linear-gradient(145deg, #262C4F 0%, #161B36 100%);
border: 1.5px solid rgba(167,139,250,.45);
box-shadow: 0 0 18px rgba(124,58,237,.25), inset 0 1px 0 rgba(255,255,255,.08);
transition: transform .25s ease;
}
.professor:hover .prof-head { transform: rotate(-4deg); }
.prof-eye {
width: 17px; height: 19px; margin-top: -4px;
border-radius: 50%; background: #EAF2FF;
position: relative; overflow: hidden;
animation: prof-blink 5.2s infinite;
}
.prof-pupil {
position: absolute; left: 50%; top: 50%;
width: 8px; height: 8px; margin: -4px 0 0 -4px;
border-radius: 50%; background: #131A36;
transition: transform 0.1s ease;
}
.prof-pupil::after {
content: ''; position: absolute; top: 1px; left: 1.5px;
width: 2.5px; height: 2.5px; border-radius: 50%;
background: #fff; opacity: .9;
}
.prof-mouth {
position: absolute; bottom: 8px; left: 50%;
transform: translateX(-50%);
width: 16px; height: 7px;
border: 2px solid var(--cyan-l); border-top: none;
border-radius: 0 0 14px 14px;
opacity: .85; transition: width .2s, height .2s;
}
.professor:hover .prof-mouth { width: 22px; height: 10px; }
.prof-body {
width: 54px; height: 38px; margin: -3px auto 0; position: relative;
border-radius: 14px 14px 16px 16px;
background: linear-gradient(160deg, #2E2553 0%, #173A52 100%);
border: 1.5px solid rgba(103,232,249,.35);
box-shadow: inset 0 1px 0 rgba(255,255,255,.07);
}
.prof-chest {
position: absolute; top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 12px; height: 12px; border-radius: 50%;
background: radial-gradient(circle at 35% 35%, var(--cyan-l), var(--purple));
box-shadow: 0 0 12px rgba(103,232,249,.7);
animation: pulse 2.8s ease-in-out infinite;
}
.prof-arm {
position: absolute; top: 4px;
width: 9px; height: 26px; border-radius: 6px;
background: linear-gradient(180deg, #2E2553, #1D2348);
border: 1.5px solid rgba(167,139,250,.3);
transform-origin: top center;
transition: transform .25s ease;
}
.prof-arm.left { left: -12px; transform: rotate(14deg); }
.prof-arm.right { right: -12px; transform: rotate(-14deg); }
.professor:hover .prof-arm.right { animation: prof-wave .8s ease-in-out infinite; }
/* ── Library wings β€” full-height public-library decor on both edges ──── */
/* Positioned absolute (not fixed: Gradio ancestor transforms break fixed)
and re-parented onto <body> by BRIDGE_JS, which also stretches the
height to the full page and clones .lib-unit blocks to fill it. */
.library {
position: absolute; top: 0; z-index: 0;
/* stretch right up to the 760px centre column (20px breathing room) */
width: max(190px, calc((100vw - 800px) / 2));
height: 100vh;
background:
radial-gradient(ellipse 90% 16% at 50% -3%, rgba(167,139,250,.10), transparent 60%),
linear-gradient(180deg, #0B1126 0%, #0A0F1E 55%, #0C1226 100%);
overflow: hidden;
/* no entrance animation: the BRIDGE_JS re-parent would restart it */
}
.lib-unit {
height: 950px;
display: flex; flex-direction: column; justify-content: space-between;
gap: 12px; padding: 26px 18px;
}
.library-left { left: 0; border-right: 1px solid var(--border); box-shadow: 18px 0 40px -20px rgba(0,0,0,.6); }
.library-right { right: 0; border-left: 1px solid var(--border); box-shadow: -18px 0 40px -20px rgba(0,0,0,.6); }
@media (max-width: 1180px) { .library { display: none; } }
/* bookshelf rows β€” books bottom-align on a wood plank */
.lib-shelf {
position: relative; height: 76px; flex-shrink: 0;
display: flex; align-items: flex-end; gap: 4px; padding: 0 8px;
border-bottom: 7px solid #3B2D23;
box-shadow: 0 9px 12px -7px rgba(0,0,0,.6);
overflow: hidden; /* shelves are over-filled; surplus books clip at the edge */
}
.lib-shelf.pad-l { padding-left: 64px; } /* room for a robot on the left */
.lib-shelf.pad-r { padding-right: 64px; } /* room for a robot / ladder on the right */
.bk {
display: block; flex-shrink: 0;
width: 12px; height: var(--h, 40px);
background: var(--c, #4C3D8F);
border-radius: 2px 2px 0 0;
box-shadow: inset -2px 0 0 rgba(0,0,0,.35), inset 0 2px 0 rgba(255,255,255,.18);
}
.bk.slim { width: 9px; }
.bk.wide { width: 16px; }
.bk.lean {
transform: rotate(-9deg);
transform-origin: bottom right;
margin-left: -2px;
}
.b1 { --c: #7C3AED; } .b2 { --c: #A78BFA; } .b3 { --c: #06B6D4; } .b4 { --c: #67E8F9; }
.b5 { --c: #E2E8F0; } .b6 { --c: #4C3D8F; } .b7 { --c: #1E6F8F; } .b8 { --c: #2E2553; }
/* aisle β€” perspective corridor between two shelf walls */
.lib-aisle {
position: relative; flex: 0 1 150px; min-height: 96px;
border-radius: 10px; border: 1px solid var(--border);
background: linear-gradient(180deg, #0A0F1E, #10172E);
overflow: hidden;
}
.lib-aisle::before, .lib-aisle::after {
content: ''; position: absolute; top: 0; bottom: 0; width: 50%;
background:
repeating-linear-gradient(0deg, rgba(59,45,35,.95) 0 5px, transparent 5px 26px),
repeating-linear-gradient(90deg,
#4C3D8F 0 6px, #06B6D4 6px 10px, #2E2553 10px 17px,
#A78BFA 17px 21px, #1E2240 21px 30px);
}
.lib-aisle::before { left: 0; transform: perspective(240px) rotateY(50deg); transform-origin: left center; }
.lib-aisle::after { right: 0; transform: perspective(240px) rotateY(-50deg); transform-origin: right center; }
.aisle-end { /* glowing far end + floor */
position: absolute; inset: 0; pointer-events: none;
background:
radial-gradient(ellipse 26% 40% at 50% 46%, rgba(167,139,250,.30), transparent 70%),
linear-gradient(180deg, transparent 58%, rgba(20,26,51,.9) 60%, #141A33 100%);
}
.aisle-bot { /* tiny robot deep in the corridor */
position: absolute; bottom: 26%; left: 50%; margin-left: -6px;
width: 12px; height: 17px; border-radius: 4px 4px 3px 3px;
background: linear-gradient(180deg, #262C4F 0 45%, #173A52 45% 100%);
border: 1px solid rgba(167,139,250,.4);
animation: aisle-walk 9s ease-in-out infinite;
}
.aisle-bot::before { /* visor */
content: ''; position: absolute; top: 4px; left: 2px; right: 2px; height: 3px;
border-radius: 2px; background: rgba(234,242,255,.85);
}
/* tall section β€” shelf with room below for ladder / fetching robot */
.lib-tall { position: relative; flex: 0 1 200px; min-height: 150px; }
.lib-tall .lib-shelf { position: absolute; top: 0; left: 0; right: 0; }
.lib-ladder {
position: absolute; bottom: 4px; right: 24px;
width: 32px; height: 126px;
transform: rotate(7deg); transform-origin: bottom center;
border-left: 4px solid #4A372A; border-right: 4px solid #4A372A;
background: repeating-linear-gradient(180deg, transparent 0 15px, #4A372A 15px 19px);
border-radius: 3px; opacity: .9;
}
/* study desk β€” robot working behind it */
.lib-desk { position: relative; height: 92px; flex-shrink: 0; }
.desk-table { position: absolute; bottom: 6px; left: 14%; right: 14%; height: 36px; }
.desk-table::before { /* wood top */
content: ''; position: absolute; top: 0; left: 0; right: 0; height: 7px;
background: linear-gradient(180deg, #4A372A, #33261D);
border-radius: 3px;
box-shadow: 0 3px 6px rgba(0,0,0,.45);
}
.desk-table::after { /* front panel */
content: ''; position: absolute; top: 7px; left: 7px; right: 7px; bottom: 0;
background: linear-gradient(180deg, #2A2019, #1C1612);
border-radius: 0 0 4px 4px;
}
.desk-screen {
position: absolute; bottom: 48px; left: 58%;
width: 17px; height: 12px; transform: skewX(-6deg);
background: linear-gradient(180deg, #67E8F9, #0E7490);
border-radius: 2px 2px 1px 1px;
box-shadow: 0 0 10px rgba(103,232,249,.55);
animation: pulse 1.7s ease-in-out infinite;
}
.desk-pile { position: absolute; bottom: 48px; left: 58%; width: 20px; }
.desk-pile i {
display: block; height: 5px; margin-top: 2px; border-radius: 2px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.25);
}
/* potted plant on a shelf */
.lib-plant { position: absolute; bottom: 0; right: 14px; width: 20px; height: 34px; }
.lib-plant::before {
content: ''; position: absolute; bottom: 9px; left: 50%; margin-left: -10px;
width: 20px; height: 20px;
background: radial-gradient(circle at 50% 90%, #10B981 0%, #0A7D5D 75%);
border-radius: 50% 50% 46% 46% / 76% 76% 24% 24%;
box-shadow: -5px 3px 0 -3px #0E9F77, 5px 3px 0 -3px #0E9F77;
}
.lib-plant::after {
content: ''; position: absolute; bottom: 0; left: 50%; margin-left: -7px;
width: 14px; height: 10px;
background: linear-gradient(180deg, #4A372A, #33261D);
clip-path: polygon(0 0, 100% 0, 82% 100%, 18% 100%);
border-radius: 2px;
}
/* open floor at the bottom β€” patrol area */
.lib-floor { position: relative; flex: 1 0 96px; }
/* ── Mini robots ──────────────────────────────────────────────────────── */
.mini-bot {
position: absolute; width: 44px;
filter: drop-shadow(0 4px 6px rgba(0,0,0,.5));
}
.mini-head {
width: 28px; height: 21px; margin: 0 auto; position: relative;
display: flex; align-items: center; justify-content: center; gap: 5px;
border-radius: 9px;
background: linear-gradient(145deg, #262C4F, #161B36);
border: 1px solid rgba(167,139,250,.45);
}
.mini-head::before { /* antenna stem */
content: ''; position: absolute; top: -6px; left: 50%; margin-left: -1px;
width: 2px; height: 6px; border-radius: 1px;
background: var(--purple-l);
}
.mini-head::after { /* antenna tip */
content: ''; position: absolute; top: -10px; left: 50%; margin-left: -2.5px;
width: 5px; height: 5px; border-radius: 50%;
background: var(--cyan-l);
box-shadow: 0 0 6px rgba(103,232,249,.8);
}
.mini-eye { width: 5px; height: 6px; border-radius: 50%; background: #EAF2FF; }
.mini-body {
width: 22px; height: 14px; margin: -2px auto 0;
border-radius: 6px;
background: linear-gradient(160deg, #2E2553, #173A52);
border: 1px solid rgba(103,232,249,.35);
}
/* walking patrol along the floor, flipping at each end */
.bot-walker { bottom: 10px; animation: bot-walk 14s ease-in-out infinite; }
.bot-carrier { bottom: 10px; animation: bot-walk 18s ease-in-out -6s infinite; }
.bot-walker .mini-head, .bot-carrier .mini-head,
.bot-worker .mini-head { animation: mini-bob .6s ease-in-out infinite; }
/* stack of books carried in front */
.bot-stack {
position: absolute; bottom: 2px; left: 50%; margin-left: -13px;
width: 26px; z-index: 2;
}
.bot-stack i {
display: block; height: 6px; margin-top: 2px; border-radius: 2px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.4);
}
/* sitting on a shelf, reading β€” gentle rocking, book held in front */
.bot-reader {
bottom: 0; left: 10px;
transform-origin: bottom center;
animation: bot-read 3.4s ease-in-out infinite;
}
.bot-reader::after {
content: ''; position: absolute; bottom: 1px; left: 50%; margin-left: -12px;
width: 24px; height: 13px;
background: linear-gradient(90deg, var(--purple) 0 47%, #E2E8F0 47% 53%, var(--cyan) 53% 100%);
border-radius: 2px 2px 5px 5px;
transform: perspective(40px) rotateX(28deg);
box-shadow: 0 2px 4px rgba(0,0,0,.45);
}
/* dozing on a shelf β€” closed eyes, breathing pulse, floating zzz */
.bot-sleeper {
bottom: 0; right: 12px;
transform-origin: bottom center;
animation: bot-sleep 3.8s ease-in-out infinite;
}
.bot-sleeper .mini-eye { height: 1.5px; border-radius: 1px; }
.zz {
position: absolute; bottom: 100%; left: 100%;
font-size: 10px; font-weight: 800; line-height: 1;
color: var(--cyan-l); opacity: 0;
animation: zz-float 2.7s ease-out infinite;
}
.zz:nth-of-type(2) { animation-delay: .9s; font-size: 12px; }
.zz:nth-of-type(3) { animation-delay: 1.8s; font-size: 14px; }
/* climbing the ladder to fetch a book */
.bot-climber { right: 22px; bottom: 6px; animation: bot-climb 8s ease-in-out infinite; }
/* on tiptoes, pulling a book down from the shelf above */
.bot-fetcher {
left: 18px; bottom: 6px;
transform-origin: bottom center;
animation: bot-fetch 4.6s ease-in-out infinite;
}
.bot-fetcher::before { /* the coveted book */
content: ''; position: absolute; top: -13px; left: 50%; margin-left: -9px;
width: 18px; height: 6px; border-radius: 2px;
background: var(--purple);
box-shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 3px rgba(0,0,0,.4);
}
/* seated at the desk, body hidden behind the front panel */
.bot-worker { left: 26%; bottom: 30px; }
/* ── Hero text overlay ────────────────────────────────────────────────── */
.hero-overlay {
position: absolute; top: 30px; left: 0; right: 0; z-index: 4;
text-align: center; pointer-events: none;
}
.hero-title {
font-size: clamp(2.2rem, 6vw, 3.4rem);
font-weight: 900; line-height: 1;
background: linear-gradient(135deg, var(--purple-l) 0%, var(--cyan-l) 100%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: -1.5px;
margin-bottom: 8px;
filter: drop-shadow(0 4px 18px rgba(124,58,237,.35));
animation: fadeUp .7s .15s ease both;
}
.hero-sub {
color: var(--muted); font-size: .98rem; font-weight: 400;
letter-spacing: .01em;
animation: fadeUp .7s .35s ease both;
}
/* ── Card ─────────────────────────────────────────────────────────────── */
.card {
background: rgba(10,15,30,0.65);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: 1px solid var(--border);
border-radius: var(--r);
padding: 28px;
box-shadow: 0 8px 32px rgba(0,0,0,.45), 0 1px 0 rgba(255,255,255,.04) inset;
transition: border-color .25s;
}
.card:hover { border-color: rgba(255,255,255,.12); }
/* ── Buttons ──────────────────────────────────────────────────────────── */
button { cursor: pointer; font-family: inherit; font-size: .95rem;
border: none; outline: none; transition: all .2s; }
.btn-primary {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
padding: 12px 28px; border-radius: 50px; font-weight: 700;
background: linear-gradient(135deg, var(--purple) 0%, var(--cyan) 100%);
color: #fff;
box-shadow: 0 4px 20px rgba(124,58,237,.35);
}
.btn-primary:hover:not(:disabled) {
transform: translateY(-1px) scale(1.02);
box-shadow: 0 6px 28px rgba(124,58,237,.5);
}
.btn-primary:active:not(:disabled) { transform: scale(.98); }
.btn-primary:disabled { opacity: .4; cursor: not-allowed; }
.btn-ghost {
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
padding: 10px 22px; border-radius: 50px; font-weight: 600;
background: transparent; color: var(--muted);
border: 1px solid var(--border);
}
.btn-ghost:hover:not(:disabled) {
background: var(--surface-2); color: var(--text);
border-color: rgba(255,255,255,.2);
}
.btn-ghost:disabled { opacity: .35; cursor: not-allowed; }
/* ── Pill toggles ─────────────────────────────────────────────────────── */
.toggle-group { display: flex; gap: 6px; }
.pill {
padding: 6px 16px; border-radius: 50px; font-size: .82rem; font-weight: 600;
border: 1px solid var(--border); color: var(--muted);
background: transparent; transition: all .18s;
}
.pill:hover { background: var(--surface-2); color: var(--text); border-color: rgba(255,255,255,.2); }
.pill.active {
background: linear-gradient(135deg, var(--purple), var(--cyan));
border-color: transparent; color: #fff;
box-shadow: 0 2px 12px rgba(124,58,237,.4);
}
/* ── Quiz controls row ───────────────────────────────────────────────── */
.controls-row {
display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
margin-bottom: 24px;
}
/* ── Mode selector cards ─────────────────────────────────────────────── */
.mode-selector {
display: flex; gap: 10px; width: 100%; margin-bottom: 20px;
}
.mode-card {
flex: 1; display: flex; align-items: center; gap: 12px;
padding: 14px 16px; border-radius: var(--r-sm);
border: 2px solid var(--border);
background: var(--surface);
cursor: pointer; transition: all .2s; text-align: left;
font-family: inherit;
}
.mode-card:hover { border-color: rgba(255,255,255,.2); background: var(--surface-2); }
.mode-card.selected {
border-color: var(--purple);
background: rgba(124,58,237,.12);
box-shadow: 0 0 0 1px rgba(124,58,237,.3), 0 4px 16px rgba(124,58,237,.2);
}
.mode-card-icon {
font-size: 1.5rem; flex-shrink: 0;
width: 40px; height: 40px;
border-radius: 10px; background: rgba(255,255,255,.06);
display: flex; align-items: center; justify-content: center;
transition: background .2s;
}
.mode-card.selected .mode-card-icon { background: rgba(124,58,237,.25); }
.mode-card-body { flex: 1; }
.mode-card-title {
font-size: .92rem; font-weight: 700; color: var(--muted);
margin-bottom: 2px; transition: color .2s;
}
.mode-card.selected .mode-card-title { color: var(--text); }
.mode-card-desc { font-size: .75rem; color: var(--dim); line-height: 1.3; }
.mode-card-check {
width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0;
border: 2px solid var(--border);
display: flex; align-items: center; justify-content: center;
font-size: .7rem; color: transparent;
transition: all .2s;
}
.mode-card.selected .mode-card-check {
background: var(--purple); border-color: var(--purple); color: #fff;
}
.diff-label { font-size: .75rem; color: var(--dim); font-weight: 500;
letter-spacing: .04em; margin-bottom: 4px; }
/* ── Score ring ───────────────────────────────────────────────────────── */
.score-wrap { position: relative; width: 56px; height: 56px; flex-shrink: 0; }
.score-ring { width: 56px; height: 56px; }
.score-label {
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
font-size: .72rem; font-weight: 700; color: var(--text);
letter-spacing: -.5px;
}
#score-arc { transition: stroke-dashoffset .6s cubic-bezier(.4,0,.2,1); }
/* ── Question card ────────────────────────────────────────────────────── */
.q-label {
font-size: .72rem; font-weight: 700; letter-spacing: .1em;
text-transform: uppercase; color: var(--purple-l); margin-bottom: 10px;
}
.q-text {
font-size: 1.1rem; font-weight: 600; line-height: 1.55;
color: var(--text); min-height: 3.2em;
}
.q-inner {
background: rgba(124,58,237,.07); border: 1px solid rgba(124,58,237,.2);
border-radius: var(--r-sm); padding: 18px 20px; margin-bottom: 20px;
}
/* ── Answer area ──────────────────────────────────────────────────────── */
.answer-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 8px;
}
.answer-header label { font-size: .85rem; font-weight: 600; color: var(--muted); }
.char-count { font-size: .78rem; color: var(--dim); font-variant-numeric: tabular-nums; }
textarea.answer-input {
width: 100%; resize: vertical; min-height: 110px;
background: rgba(255,255,255,.04); border: 1px solid var(--border);
border-radius: var(--r-sm); padding: 14px 16px;
color: var(--text); font-family: inherit; font-size: .95rem; line-height: 1.5;
outline: none; transition: border-color .2s, box-shadow .2s;
}
textarea.answer-input:focus {
border-color: var(--purple); box-shadow: 0 0 0 3px rgba(124,58,237,.15);
}
textarea.answer-input::placeholder { color: var(--dim); }
.answer-actions {
display: flex; justify-content: flex-end; gap: 10px; margin-top: 14px;
flex-wrap: wrap;
}
/* ── No question prompt ───────────────────────────────────────────────── */
.empty-state {
text-align: center; padding: 32px 0;
color: var(--muted); font-size: .95rem;
}
.empty-state p { margin-bottom: 18px; }
/* ── End session button ───────────────────────────────────────────────── */
.end-row { margin-top: 20px; display: flex; justify-content: center; }
/* ── Spinner ──────────────────────────────────────────────────────────── */
.spinner {
width: 16px; height: 16px; border-radius: 50%;
border: 2px solid rgba(255,255,255,.25);
border-top-color: #fff;
animation: spin .65s linear infinite;
display: inline-block; flex-shrink: 0;
}
/* ── Feedback card ────────────────────────────────────────────────────── */
.feedback-header {
display: flex; align-items: center; gap: 12px; margin-bottom: 18px;
}
.verdict-badge {
padding: 6px 18px; border-radius: 50px;
font-size: .82rem; font-weight: 800; letter-spacing: .05em;
text-transform: uppercase; flex-shrink: 0;
animation: popIn .35s cubic-bezier(.34,1.56,.64,1) both;
}
.verdict-badge.correct { background: rgba(16,185,129,.18); border: 1px solid var(--green); color: #6EE7B7; }
.verdict-badge.partial { background: rgba(245,158,11,.18); border: 1px solid var(--amber); color: #FCD34D; }
.verdict-badge.incorrect { background: rgba(239,68,68,.18); border: 1px solid var(--red); color: #FCA5A5; }
.feedback-body {
font-size: .95rem; line-height: 1.7; color: var(--text);
}
.feedback-body .section { margin-bottom: 12px; }
.feedback-body .section-num {
font-size: .75rem; font-weight: 700; letter-spacing: .08em;
text-transform: uppercase; color: var(--muted); display: block;
margin-bottom: 4px;
}
.feedback-body .section-content { color: var(--text); }
.feedback-raw { white-space: pre-wrap; }
/* ── Session image (modal) ────────────────────────────────────────────── */
#modal-session-image {
margin-bottom: 24px; padding: 18px;
background: rgba(255,255,255,.04);
border: 1px solid var(--border);
border-radius: var(--r-sm);
backdrop-filter: blur(12px);
text-align: center;
}
.concept-image-title {
font-size: .72rem; font-weight: 700; letter-spacing: .1em;
text-transform: uppercase; color: var(--cyan-l);
margin-bottom: 14px; text-align: left;
}
#session-image {
width: 100%; max-width: 320px; display: inline-block;
border-radius: var(--r-sm); border: 1px solid var(--border);
box-shadow: 0 8px 24px rgba(0,0,0,.4);
}
.concept-image-caption {
margin-top: 10px; font-size: .8rem; color: var(--muted);
}
/* ── Collapsible source ───────────────────────────────────────────────── */
details.source-details { margin-top: 18px; }
details.source-details summary {
cursor: pointer; color: var(--dim); font-size: .82rem; font-weight: 600;
letter-spacing: .03em; list-style: none; user-select: none;
transition: color .2s;
}
details.source-details summary::before { content: 'β–Ά '; font-size: .7em; }
details.source-details[open] summary::before { content: 'β–Ό '; }
details.source-details summary:hover { color: var(--muted); }
.source-text {
margin-top: 10px; padding: 14px 16px;
background: rgba(255,255,255,.03); border: 1px solid var(--border);
border-radius: var(--r-sm);
font-size: .82rem; line-height: 1.6; color: var(--muted);
max-height: 180px; overflow-y: auto;
}
/* ── Modal ────────────────────────────────────────────────────────────── */
.modal-overlay {
position: fixed; inset: 0; z-index: 100;
background: rgba(0,0,0,.7); backdrop-filter: blur(8px);
display: flex; align-items: center; justify-content: center;
padding: 20px;
animation: fadeIn .2s ease both;
}
.modal-card {
background: rgba(15,20,40,.95); border: 1px solid rgba(124,58,237,.3);
border-radius: 24px; padding: 40px 36px;
max-width: 420px; width: 100%; text-align: center;
box-shadow: 0 0 60px rgba(124,58,237,.25), 0 20px 60px rgba(0,0,0,.6);
animation: popIn .4s cubic-bezier(.34,1.56,.64,1) both;
}
.modal-icon { font-size: 3.5rem; display: block; margin-bottom: 16px;
filter: drop-shadow(0 0 12px rgba(124,58,237,.5)); }
.modal-title { font-size: 1.7rem; font-weight: 800; margin-bottom: 20px;
background: linear-gradient(135deg, var(--purple-l), var(--cyan-l));
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text; }
.modal-score-big {
font-size: 3.5rem; font-weight: 900; line-height: 1;
background: linear-gradient(135deg, var(--purple-l), var(--cyan-l));
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text; margin-bottom: 8px;
}
.modal-score-label { color: var(--muted); font-size: .9rem; margin-bottom: 20px; }
.modal-message { color: var(--muted); font-size: .95rem; line-height: 1.5;
margin-bottom: 28px; }
.modal-history { text-align: left; margin-bottom: 24px; }
.modal-history-item {
display: flex; align-items: flex-start; gap: 10px;
padding: 8px 0; border-bottom: 1px solid var(--border); font-size: .85rem;
}
.modal-history-item:last-child { border-bottom: none; }
.hist-badge {
flex-shrink: 0; padding: 2px 10px; border-radius: 50px; font-size: .72rem;
font-weight: 700; text-transform: uppercase;
}
.hist-badge.correct { background: rgba(16,185,129,.2); color: #6EE7B7; }
.hist-badge.partial { background: rgba(245,158,11,.2); color: #FCD34D; }
.hist-badge.incorrect { background: rgba(239,68,68,.2); color: #FCA5A5; }
.hist-q { color: var(--muted); flex: 1; }
/* ── Loading overlay (inside card) ───────────────────────────────────── */
.loading-msg {
display: flex; align-items: center; justify-content: center; gap: 10px;
padding: 14px; color: var(--muted); font-size: .9rem;
}
/* ── Animations ───────────────────────────────────────────────────────── */
@keyframes fadeUp { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes popIn { from { opacity: 0; transform: scale(.8); } to { opacity: 1; transform: scale(1); } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }
@keyframes twinkle { 0%, 100% { opacity: .9; } 50% { opacity: .25; } }
@keyframes prof-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }
@keyframes prof-blink { 0%, 90%, 100% { transform: scaleY(1); } 93% { transform: scaleY(.12); } 96% { transform: scaleY(1); } }
@keyframes prof-wave {
0%, 100% { transform: rotate(-160deg); }
50% { transform: rotate(-115deg); }
}
@keyframes bot-walk {
0% { left: 10px; transform: scaleX(1); }
44% { left: calc(100% - 56px); transform: scaleX(1); }
50% { left: calc(100% - 56px); transform: scaleX(-1); }
94% { left: 10px; transform: scaleX(-1); }
100% { left: 10px; transform: scaleX(1); }
}
@keyframes bot-climb {
0%, 12% { transform: translate(0, 0); }
42%, 58% { transform: translate(-13px, -88px); }
88%, 100% { transform: translate(0, 0); }
}
@keyframes bot-fetch { 0%, 100% { transform: scaleY(1); } 45%, 60% { transform: scaleY(1.13); } }
@keyframes aisle-walk {
0%, 100% { transform: translateX(-9px) scale(.92); }
50% { transform: translateX(9px) scale(1); }
}
@keyframes mini-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1.5px); } }
@keyframes bot-read { 0%, 100% { transform: rotate(-2deg); } 50% { transform: rotate(2.5deg); } }
@keyframes bot-sleep { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
@keyframes zz-float {
0% { transform: translate(0, 0); opacity: 0; }
20% { opacity: .9; }
100% { transform: translate(10px, -24px); opacity: 0; }
}
.hidden { display: none !important; }
.fade-in-up { animation: fadeUp .4s ease both; }
/* ── MCQ options ─────────────────────────────────────────────────────── */
.mcq-options { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.mcq-btn {
display: flex; align-items: center; gap: 14px;
padding: 14px 18px; border-radius: var(--r-sm);
background: var(--surface); border: 1px solid var(--border);
color: var(--text); text-align: left; font-size: .95rem; font-weight: 500;
width: 100%; transition: all .18s;
}
.mcq-btn:hover:not(:disabled) {
background: var(--surface-2); border-color: rgba(255,255,255,.2);
transform: translateX(4px);
}
.mcq-btn:disabled { cursor: default; }
.mcq-btn.mcq-correct { background: rgba(16,185,129,.12); border-color: var(--green); }
.mcq-btn.mcq-wrong { background: rgba(239,68,68,.12); border-color: var(--red); }
.mcq-btn.mcq-neutral { opacity: .38; }
.mcq-letter {
flex-shrink: 0; width: 28px; height: 28px; border-radius: 50%;
background: rgba(124,58,237,.2); border: 1px solid rgba(124,58,237,.3);
display: flex; align-items: center; justify-content: center;
font-size: .78rem; font-weight: 800; color: var(--purple-l);
}
.mcq-btn.mcq-correct .mcq-letter { background: rgba(16,185,129,.25); border-color: var(--green); color: #6EE7B7; }
.mcq-btn.mcq-wrong .mcq-letter { background: rgba(239,68,68,.25); border-color: var(--red); color: #FCA5A5; }
.mcq-text { flex: 1; line-height: 1.45; text-align: left; }
/* ── MCQ explanations ────────────────────────────────────────────────── */
.mcq-explanations { display: flex; flex-direction: column; gap: 8px; margin-top: 18px; }
.mcq-exp {
display: flex; align-items: flex-start; gap: 10px;
padding: 10px 14px; border-radius: var(--r-sm);
font-size: .87rem; line-height: 1.5;
}
.exp-correct { background: rgba(16,185,129,.08); border: 1px solid rgba(16,185,129,.2); }
.exp-wrong { background: rgba(239,68,68,.08); border: 1px solid rgba(239,68,68,.2); }
.exp-neutral { background: var(--surface); border: 1px solid var(--border); }
.mcq-exp-letter {
flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: .72rem; font-weight: 800;
}
.exp-correct .mcq-exp-letter { background: rgba(16,185,129,.25); color: #6EE7B7; }
.exp-wrong .mcq-exp-letter { background: rgba(239,68,68,.25); color: #FCA5A5; }
.exp-neutral .mcq-exp-letter { background: rgba(255,255,255,.1); color: var(--muted); }
.mcq-exp-text { color: var(--muted); flex: 1; }
/* ── Responsive ───────────────────────────────────────────────────────── */
@media (max-width: 520px) {
#app { padding: 20px 14px 60px; }
.card { padding: 20px 18px; }
.hero { padding: 8px 0 0; }
.amphitheater { height: 300px; }
.hero-overlay { top: 20px; }
.hero-title { font-size: 2.2rem; }
.controls-row { gap: 8px; }
.answer-actions { flex-direction: column; }
.answer-actions button { width: 100%; justify-content: center; }
}
</style>
<canvas id="particles"></canvas>
<!-- ── Library wings β€” page-height decor flanking the app (decorative).
BRIDGE_JS re-parents these onto <body>, sizes them to the full page
and clones .lib-unit as many times as needed to reach the bottom. ──── -->
<div class="library library-left" aria-hidden="true">
<div class="lib-unit">
<div class="lib-shelf">
<i class="bk b2" style="--h:36px"></i><i class="bk b3" style="--h:50px"></i><i class="bk b1 slim" style="--h:56px"></i><i class="bk b6" style="--h:40px"></i>
<i class="bk b1 lean" style="--h:34px"></i><i class="bk b6" style="--h:34px"></i><i class="bk b7 slim" style="--h:44px"></i><i class="bk b4" style="--h:46px"></i>
<i class="bk b3 slim" style="--h:28px"></i><i class="bk b8 slim lean" style="--h:50px"></i><i class="bk b3" style="--h:52px"></i><i class="bk b4" style="--h:40px"></i>
<i class="bk b1" style="--h:38px"></i><i class="bk b6" style="--h:28px"></i><i class="bk b7" style="--h:30px"></i><i class="bk b4" style="--h:36px"></i>
<i class="bk b8 slim" style="--h:56px"></i><i class="bk b7" style="--h:34px"></i><i class="bk b6" style="--h:48px"></i><i class="bk b2 slim" style="--h:30px"></i>
<i class="bk b8" style="--h:36px"></i><i class="bk b4 slim" style="--h:38px"></i><i class="bk b2" style="--h:34px"></i><i class="bk b7" style="--h:56px"></i>
<i class="bk b6 slim" style="--h:46px"></i><i class="bk b5 slim" style="--h:32px"></i><i class="bk b4" style="--h:56px"></i><i class="bk b7 slim" style="--h:34px"></i>
<i class="bk b6" style="--h:52px"></i><i class="bk b8 lean" style="--h:54px"></i>
</div>
<div class="lib-shelf pad-l">
<i class="bk b6" style="--h:30px"></i><i class="bk b2 wide" style="--h:46px"></i><i class="bk b7" style="--h:48px"></i><i class="bk b4" style="--h:56px"></i>
<i class="bk b7 lean" style="--h:36px"></i><i class="bk b6" style="--h:36px"></i><i class="bk b7" style="--h:56px"></i><i class="bk b5" style="--h:34px"></i>
<i class="bk b2" style="--h:30px"></i><i class="bk b8 lean" style="--h:30px"></i><i class="bk b2 slim" style="--h:40px"></i><i class="bk b6" style="--h:40px"></i>
<i class="bk b5 wide" style="--h:44px"></i><i class="bk b3 wide" style="--h:54px"></i><i class="bk b1 slim" style="--h:30px"></i><i class="bk b6 slim" style="--h:52px"></i>
<i class="bk b7" style="--h:36px"></i><i class="bk b4" style="--h:28px"></i><i class="bk b7 wide" style="--h:36px"></i><i class="bk b5 slim" style="--h:44px"></i>
<i class="bk b8 slim" style="--h:54px"></i><i class="bk b6" style="--h:34px"></i><i class="bk b2" style="--h:32px"></i><i class="bk b6 wide" style="--h:56px"></i>
<i class="bk b5 wide lean" style="--h:46px"></i><i class="bk b1" style="--h:38px"></i><i class="bk b8 slim" style="--h:34px"></i><i class="bk b1" style="--h:46px"></i>
<div class="mini-bot bot-reader"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
</div>
<div class="lib-aisle"><div class="aisle-end"></div><span class="aisle-bot"></span></div>
<div class="lib-tall">
<div class="lib-shelf pad-r">
<i class="bk b2" style="--h:42px"></i><i class="bk b8" style="--h:52px"></i><i class="bk b5 slim" style="--h:32px"></i><i class="bk b7" style="--h:44px"></i>
<i class="bk b2" style="--h:54px"></i><i class="bk b3 wide" style="--h:52px"></i><i class="bk b7 slim" style="--h:50px"></i><i class="bk b3" style="--h:48px"></i>
<i class="bk b7" style="--h:56px"></i><i class="bk b5 lean" style="--h:34px"></i><i class="bk b3" style="--h:44px"></i><i class="bk b2" style="--h:28px"></i>
<i class="bk b1 slim" style="--h:28px"></i><i class="bk b3" style="--h:28px"></i><i class="bk b8 lean" style="--h:44px"></i><i class="bk b6" style="--h:44px"></i>
<i class="bk b2 slim" style="--h:56px"></i><i class="bk b6" style="--h:34px"></i><i class="bk b8" style="--h:40px"></i><i class="bk b2 lean" style="--h:48px"></i>
<i class="bk b5" style="--h:54px"></i><i class="bk b7" style="--h:48px"></i><i class="bk b6" style="--h:40px"></i><i class="bk b7" style="--h:54px"></i>
<i class="bk b1 lean" style="--h:34px"></i><i class="bk b3" style="--h:36px"></i><i class="bk b5" style="--h:56px"></i><i class="bk b1" style="--h:54px"></i>
</div>
<div class="lib-ladder"></div>
<div class="mini-bot bot-climber"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
</div>
<div class="lib-desk">
<div class="mini-bot bot-worker"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
<div class="desk-table"></div>
<div class="desk-screen"></div>
</div>
<div class="lib-floor">
<div class="mini-bot bot-walker"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
</div>
</div>
</div>
<div class="library library-right" aria-hidden="true">
<div class="lib-unit">
<div class="lib-shelf pad-r">
<i class="bk b2" style="--h:44px"></i><i class="bk b8" style="--h:30px"></i><i class="bk b7 slim" style="--h:32px"></i><i class="bk b4" style="--h:34px"></i>
<i class="bk b8 lean" style="--h:28px"></i><i class="bk b1 wide" style="--h:36px"></i><i class="bk b8 slim" style="--h:36px"></i><i class="bk b4 slim" style="--h:50px"></i>
<i class="bk b8" style="--h:50px"></i><i class="bk b4 lean" style="--h:36px"></i><i class="bk b1" style="--h:44px"></i><i class="bk b2 slim" style="--h:28px"></i>
<i class="bk b1" style="--h:44px"></i><i class="bk b8" style="--h:28px"></i><i class="bk b5 lean" style="--h:32px"></i><i class="bk b1 slim" style="--h:34px"></i>
<i class="bk b5" style="--h:56px"></i><i class="bk b6" style="--h:46px"></i><i class="bk b1 slim" style="--h:40px"></i><i class="bk b7 lean" style="--h:44px"></i>
<i class="bk b3" style="--h:50px"></i><i class="bk b4" style="--h:40px"></i><i class="bk b2 slim" style="--h:36px"></i><i class="bk b5" style="--h:52px"></i>
<i class="bk b1" style="--h:46px"></i><i class="bk b2" style="--h:34px"></i><i class="bk b6" style="--h:56px"></i><i class="bk b3 wide" style="--h:56px"></i>
<div class="mini-bot bot-sleeper"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div><span class="zz">z</span><span class="zz">z</span><span class="zz">z</span></div>
</div>
<div class="lib-shelf" style="padding-right:46px">
<i class="bk b4" style="--h:32px"></i><i class="bk b5 slim" style="--h:50px"></i><i class="bk b3 slim" style="--h:52px"></i><i class="bk b7" style="--h:48px"></i>
<i class="bk b8" style="--h:56px"></i><i class="bk b2" style="--h:56px"></i><i class="bk b1 slim" style="--h:32px"></i><i class="bk b4" style="--h:34px"></i>
<i class="bk b7" style="--h:48px"></i><i class="bk b6 wide lean" style="--h:44px"></i><i class="bk b8" style="--h:48px"></i><i class="bk b4 slim" style="--h:28px"></i>
<i class="bk b1" style="--h:32px"></i><i class="bk b7 wide" style="--h:32px"></i><i class="bk b6 lean" style="--h:50px"></i><i class="bk b1" style="--h:56px"></i>
<i class="bk b7 wide" style="--h:44px"></i><i class="bk b1 slim" style="--h:46px"></i><i class="bk b6" style="--h:32px"></i><i class="bk b1" style="--h:56px"></i>
<i class="bk b8" style="--h:38px"></i><i class="bk b2 slim" style="--h:48px"></i><i class="bk b1" style="--h:44px"></i><i class="bk b8" style="--h:46px"></i>
<i class="bk b6 lean" style="--h:56px"></i><i class="bk b2 wide" style="--h:52px"></i><i class="bk b3 wide" style="--h:28px"></i><i class="bk b2 slim" style="--h:38px"></i>
<div class="lib-plant"></div>
</div>
<div class="lib-aisle"><div class="aisle-end"></div><span class="aisle-bot" style="animation-delay:-4s"></span></div>
<div class="lib-tall">
<div class="lib-shelf pad-l" style="justify-content:flex-end">
<i class="bk b7 slim" style="--h:54px"></i><i class="bk b6 slim" style="--h:36px"></i><i class="bk b2 slim" style="--h:30px"></i><i class="bk b5 wide" style="--h:54px"></i>
<i class="bk b4" style="--h:54px"></i><i class="bk b3" style="--h:52px"></i><i class="bk b8" style="--h:28px"></i><i class="bk b6" style="--h:38px"></i>
<i class="bk b3 wide" style="--h:52px"></i><i class="bk b4 lean" style="--h:44px"></i><i class="bk b8" style="--h:28px"></i><i class="bk b1 wide" style="--h:36px"></i>
<i class="bk b3" style="--h:36px"></i><i class="bk b1" style="--h:40px"></i><i class="bk b4 slim lean" style="--h:38px"></i><i class="bk b6" style="--h:56px"></i>
<i class="bk b5" style="--h:28px"></i><i class="bk b7" style="--h:44px"></i><i class="bk b8" style="--h:50px"></i><i class="bk b6 slim lean" style="--h:34px"></i>
<i class="bk b1 wide" style="--h:56px"></i><i class="bk b4 slim" style="--h:48px"></i><i class="bk b8" style="--h:56px"></i><i class="bk b3" style="--h:48px"></i>
<i class="bk b5 lean" style="--h:50px"></i><i class="bk b2" style="--h:48px"></i><i class="bk b5 slim" style="--h:56px"></i><i class="bk b2 slim" style="--h:36px"></i>
</div>
<div class="mini-bot bot-fetcher"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
</div>
<div class="lib-desk">
<div class="mini-bot bot-worker"><div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div></div>
<div class="desk-table"></div>
<div class="desk-pile"><i style="background:#06B6D4"></i><i style="background:#A78BFA"></i><i style="background:#E2E8F0"></i></div>
</div>
<div class="lib-floor">
<div class="mini-bot bot-carrier">
<div class="bot-stack"><i style="background:#7C3AED"></i><i style="background:#67E8F9"></i><i style="background:#E2E8F0"></i></div>
<div class="mini-head"><span class="mini-eye"></span><span class="mini-eye"></span></div><div class="mini-body"></div>
</div>
</div>
</div>
</div>
<div id="app">
<!-- Hero β€” cinematic amphitheater with AI professor -->
<header class="hero">
<!-- Library wing β€” left (decorative) -->
<div class="amphitheater" id="amphitheater">
<div class="amphi-beam"></div>
<!-- ceiling lights -->
<span class="amphi-light" style="left:9%; top:9%; animation-delay:0s"></span>
<span class="amphi-light" style="left:21%; top:5%; animation-delay:.7s"></span>
<span class="amphi-light" style="left:36%; top:11%; animation-delay:1.4s"></span>
<span class="amphi-light" style="left:51%; top:4%; animation-delay:.3s"></span>
<span class="amphi-light" style="left:65%; top:10%; animation-delay:1.9s"></span>
<span class="amphi-light" style="left:79%; top:6%; animation-delay:1.1s"></span>
<span class="amphi-light" style="left:91%; top:12%; animation-delay:.5s"></span>
<!-- curved seat rows, far β†’ near -->
<div class="amphi-rows">
<div class="amphi-row"></div>
<div class="amphi-row"></div>
<div class="amphi-row"></div>
<div class="amphi-row"></div>
<div class="amphi-row"></div>
</div>
<!-- glowing stage -->
<div class="amphi-stage"></div>
<div class="amphi-podium"></div>
<!-- AI professor (eyes wired to the mouse in BRIDGE_JS) -->
<div class="professor" id="professor" title="Your AI professor">
<div class="prof-antenna"></div>
<div class="prof-head">
<div class="prof-eye"><div class="prof-pupil"></div></div>
<div class="prof-eye"><div class="prof-pupil"></div></div>
<div class="prof-mouth"></div>
</div>
<div class="prof-body">
<div class="prof-arm left"></div>
<div class="prof-arm right"></div>
<div class="prof-chest"></div>
</div>
</div>
<!-- floating text overlay -->
<div class="hero-overlay">
<h1 class="hero-title">PaperProf</h1>
<p class="hero-sub">Upload your course. Get quizzed by AI.</p>
</div>
</div>
</header>
<!-- ── Quiz card ────────────────────────────────────────────────────── -->
<div class="card hidden fade-in-up" id="quiz-card" style="animation-delay:.1s">
<!-- Mode selector -->
<div class="mode-selector">
<button class="mode-card selected" id="mode-open-btn">
<div class="mode-card-icon">✏️</div>
<div class="mode-card-body">
<div class="mode-card-title">Open Question</div>
<div class="mode-card-desc">Write a free-form answer</div>
</div>
<div class="mode-card-check">βœ“</div>
</button>
<button class="mode-card" id="mode-mcq-btn">
<div class="mode-card-icon">🎯</div>
<div class="mode-card-body">
<div class="mode-card-title">Multiple Choice</div>
<div class="mode-card-desc">Pick the correct answer</div>
</div>
<div class="mode-card-check">βœ“</div>
</button>
</div>
<!-- Controls row -->
<div class="controls-row">
<!-- Score ring -->
<div class="score-wrap" style="margin-left:auto" title="Score">
<svg class="score-ring" viewBox="0 0 56 56">
<circle cx="28" cy="28" r="22" fill="none"
stroke="rgba(255,255,255,0.08)" stroke-width="4"/>
<circle id="score-arc" cx="28" cy="28" r="22" fill="none"
stroke="url(#scoreGrad)" stroke-width="4" stroke-linecap="round"
stroke-dasharray="138.2" stroke-dashoffset="138.2"
transform="rotate(-90 28 28)"/>
<defs>
<linearGradient id="scoreGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#7C3AED"/>
<stop offset="100%" stop-color="#06B6D4"/>
</linearGradient>
</defs>
</svg>
<div class="score-label" id="score-label">β€”</div>
</div>
</div>
<!-- Empty state (before first question) -->
<div class="empty-state" id="empty-state">
<p>Ready! Click to generate your first question.</p>
<button class="btn-primary" id="start-btn">
New Question
</button>
</div>
<!-- Question + answer (shown after first question) -->
<div class="hidden" id="question-area">
<div class="q-inner">
<div class="q-label">Question</div>
<p class="q-text" id="q-text"></p>
<div class="loading-msg hidden" id="q-loading">
<span class="spinner"></span>
<span id="q-loading-msg">Generating question…</span>
</div>
</div>
<!-- MCQ options (shown in MCQ mode) -->
<div class="mcq-options hidden" id="mcq-options">
<button class="mcq-btn" id="mcq-a"><span class="mcq-letter">A</span><span class="mcq-text" id="mcq-text-a"></span></button>
<button class="mcq-btn" id="mcq-b"><span class="mcq-letter">B</span><span class="mcq-text" id="mcq-text-b"></span></button>
<button class="mcq-btn" id="mcq-c"><span class="mcq-letter">C</span><span class="mcq-text" id="mcq-text-c"></span></button>
<button class="mcq-btn" id="mcq-d"><span class="mcq-letter">D</span><span class="mcq-text" id="mcq-text-d"></span></button>
</div>
<!-- Open-Q answer (hidden in MCQ mode) -->
<div id="answer-wrapper">
<div class="answer-header">
<label for="answer-input">Your Answer</label>
<span class="char-count" id="char-count">0 chars</span>
</div>
<textarea class="answer-input" id="answer-input"
placeholder="Type your answer here…" rows="4"></textarea>
</div>
<div class="answer-actions">
<button class="btn-ghost" id="new-q-btn">β†Ί New Question</button>
<button class="btn-primary" id="submit-btn" disabled>
<span id="submit-btn-text">Submit Answer</span>
<span class="spinner hidden" id="submit-spinner"></span>
</button>
</div>
</div>
<div class="end-row">
<button class="btn-ghost hidden" id="end-btn" style="font-size:.82rem;padding:7px 18px">
End Session
</button>
</div>
</div>
<!-- ── Feedback card ─────────────────────────────────────────────────── -->
<div class="card hidden" id="feedback-card">
<div class="feedback-header">
<div class="verdict-badge" id="verdict-badge"></div>
<span style="font-size:.85rem;color:var(--muted);font-weight:600">Feedback</span>
</div>
<div class="feedback-body" id="feedback-body"></div>
<div class="mcq-explanations hidden" id="mcq-explanations"></div>
</div>
</div>
<!-- ── Session modal ───────────────────────────────────────────────────── -->
<div class="modal-overlay hidden" id="modal">
<div class="modal-card">
<span class="modal-icon">πŸŽ“</span>
<h2 class="modal-title">Session Complete</h2>
<div class="modal-score-big" id="modal-score-big">β€”</div>
<div class="modal-score-label" id="modal-score-label">questions answered</div>
<p class="modal-message" id="modal-message"></p>
<div class="modal-history hidden" id="modal-history"></div>
<div class="loading-msg hidden" id="modal-image-loading">
<span class="spinner"></span> Preparing your reward…
</div>
<div class="hidden" id="modal-session-image">
<div class="concept-image-title">Your Reward</div>
<img id="session-image" alt="An encouraging illustration" />
<p class="concept-image-caption">A little encouragement from your AI professor πŸŽ‰</p>
</div>
<button class="btn-primary" id="modal-close" style="width:100%;justify-content:center">
Study Again
</button>
</div>
</div>
<script>
// Intentionally empty β€” browsers never execute <script> tags injected via
// innerHTML (gr.HTML), so ALL page logic (quiz flow, particles, professor
// eye tracking) lives in BRIDGE_JS in app.py, injected via demo.load(js=...).
void 0;
</script>