/* ============================================================ Tokens ============================================================ */ :root { --paper: #ece9e1; --surface: #fbfaf6; --ink: #1d2333; --ink-soft: #565f70; --line: #d6d0c0; --brass: #8c6a28; --brass-soft: #eee3cd; --pass: #1f6f45; --pass-bg: #e4efe7; --fail: #a32b20; --fail-bg: #f6e6e3; --font-display: "IBM Plex Serif", Georgia, serif; --font-body: "IBM Plex Sans", system-ui, sans-serif; --font-mono: "IBM Plex Mono", ui-monospace, monospace; --radius: 3px; } /* ============================================================ Reset & base ============================================================ */ * { box-sizing: border-box; } body { font-family: var(--font-body); background: var(--paper); color: var(--ink); margin: 0; line-height: 1.5; -webkit-font-smoothing: antialiased; } h1, h2, h3 { font-family: var(--font-display); font-weight: 600; margin: 0 0 0.6em; line-height: 1.2; letter-spacing: -0.01em; } h1 { font-size: 1.75rem; } h2 { font-size: 1.3rem; } h3 { font-size: 1.05rem; } p { margin: 0 0 0.75em; } ul { list-style: none; padding: 0; margin: 0; } button { font-family: var(--font-body); font-size: 0.95rem; font-weight: 500; background: var(--ink); color: var(--surface); border: 1px solid var(--ink); border-radius: var(--radius); padding: 0.55rem 1.1rem; cursor: pointer; transition: background-color 150ms ease, transform 100ms ease; } button:hover:not(:disabled) { background: #323a52; } button:active:not(:disabled) { transform: translateY(1px); } button:disabled { cursor: not-allowed; opacity: 0.55; } /* Secondary / utility buttons — outline instead of filled */ .option-row button, button[type="button"] { background: transparent; color: var(--ink); } .option-row button:hover:not(:disabled), button[type="button"]:hover:not(:disabled) { background: var(--brass-soft); } input, select, textarea { font-family: var(--font-body); font-size: 0.95rem; color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.5rem 0.6rem; } label { display: block; font-weight: 500; font-size: 0.85rem; color: var(--ink-soft); margin-top: 0.85rem; margin-bottom: 0.25rem; } /* Full-width block inputs — explicitly excludes radio/checkbox, which must keep their native size wherever they appear inside a
(option editors, true/false pickers, student checklists). */ form input:not([type="radio"]):not([type="checkbox"]), form select { display: block; width: 100%; } .option-row input[type="radio"], .option-row input[type="checkbox"] { width: auto; flex: none; } a { color: var(--brass); } :focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; } .error { color: var(--fail); background: var(--fail-bg); border-left: 3px solid var(--fail); padding: 0.5rem 0.75rem; border-radius: 0 var(--radius) var(--radius) 0; font-size: 0.9rem; } .hint { color: var(--ink-soft); font-size: 0.85rem; font-family: var(--font-mono); } /* ============================================================ Shell: topbar, page container ============================================================ */ .topbar { position: sticky; top: 0; z-index: 10; display: flex; justify-content: space-between; align-items: center; background: var(--paper); border-bottom: 1px solid var(--line); padding: 0.9rem 1.5rem; } .topbar-identity { display: flex; align-items: baseline; gap: 0.6rem; } .topbar-name { font-weight: 500; } .role-badge { font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--brass); background: var(--brass-soft); border-radius: var(--radius); padding: 0.15rem 0.5rem; } .wordmark { font-family: var(--font-display); font-weight: 600; } /* Every page under the topbar gets consistent horizontal rhythm */ .quiz-list-page, .taking-quiz-page, .result-page, .teacher-home { max-width: 880px; margin: 0 auto; padding: 1.75rem 1.5rem 3rem; } .login-page { max-width: 420px; margin: 4rem auto; padding: 0 1.5rem; } /* ============================================================ Login ============================================================ */ .login-page .wordmark { font-size: 2rem; margin-bottom: 0.25rem; } .login-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.75rem; margin-top: 1.5rem; } .login-card button[type="submit"] { width: 100%; margin-top: 1.25rem; } .login-page .hint { margin-top: 1rem; text-align: center; } /* ============================================================ Quiz list (student) ============================================================ */ .quiz-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.25rem 1.5rem; margin-bottom: 1rem; } .meta-row { display: flex; flex-wrap: wrap; gap: 1.5rem; margin: 1rem 0; padding-top: 0.85rem; border-top: 1px solid var(--line); } .meta-item { display: flex; flex-direction: column; gap: 0.15rem; } .meta-label { font-size: 0.72rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-soft); } .meta-value { font-family: var(--font-mono); font-size: 1.05rem; } /* Smaller inline mono tag — inherits color so it stays readable both in normal rows and on the dark `.selected` background of topic/quiz items. */ .tag-mono { font-family: var(--font-mono); font-size: 0.8rem; opacity: 0.72; } /* ============================================================ Taking a quiz ============================================================ */ .question { display: flex; gap: 1rem; border-top: 1px solid var(--line); padding: 1.25rem 0; } .question:first-of-type { border-top: none; } .question-number { font-family: var(--font-mono); font-size: 0.85rem; color: var(--brass); flex-shrink: 0; padding-top: 0.15rem; } .question-body { flex: 1; min-width: 0; } .question-text { font-weight: 500; margin-bottom: 0.6rem; } .option { display: flex; align-items: center; gap: 0.5rem; font-weight: normal; margin: 0 0 0.35rem; } .option input { width: auto; } .quiz-actions { position: sticky; bottom: 0; background: var(--paper); border-top: 1px solid var(--line); margin: 0 -1.5rem; padding: 1rem 1.5rem; box-shadow: 0 -6px 12px -8px rgba(29, 35, 51, 0.15); } .quiz-actions button { width: 100%; } .time-remaining { display: block; text-align: center; font-family: var(--font-mono); font-size: 0.95rem; color: var(--ink-soft); margin-bottom: 0.6rem; } .time-remaining.low { color: var(--fail); font-weight: 600; } /* ============================================================ Result ============================================================ */ .result-page { text-align: center; } .grade-stamp { display: inline-block; font-family: var(--font-display); font-weight: 600; font-size: 1.4rem; text-transform: uppercase; letter-spacing: 0.05em; border: 3px solid currentColor; border-radius: var(--radius); padding: 0.6rem 1.5rem; margin: 1rem 0 1.5rem; transform: rotate(-4deg); animation: stamp-down 260ms ease-out; } .grade-stamp.passed { color: var(--pass); background: var(--pass-bg); } .grade-stamp.failed { color: var(--fail); background: var(--fail-bg); } @keyframes stamp-down { from { opacity: 0; transform: rotate(-4deg) scale(1.4); } to { opacity: 1; transform: rotate(-4deg) scale(1); } } .score-value { display: block; font-family: var(--font-mono); font-size: 1.5rem; margin-bottom: 0.5rem; } .results-panel { margin-top: 1rem; padding-top: 0.85rem; border-top: 1px solid var(--line); } .attempt-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; padding: 0.4rem 0; } /* Compact, non-animated variant of the big result-page stamp/score, sized for a list row instead of a standalone celebratory screen. */ .attempt-row .score-value { display: inline; font-size: 0.95rem; margin-bottom: 0; } .attempt-row .grade-stamp { font-size: 0.75rem; border-width: 1px; padding: 0.15rem 0.5rem; margin: 0; transform: none; animation: none; } /* ============================================================ Teacher admin ============================================================ */ .tabs { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; border-bottom: 1px solid var(--line); } .tab-button { background: transparent; color: var(--ink-soft); border: none; border-bottom: 2px solid transparent; border-radius: 0; padding: 0.6rem 0.25rem; margin-bottom: -1px; margin-right: 1.25rem; } .tab-button:hover:not(:disabled) { background: transparent; color: var(--ink); } .tab-button.active { color: var(--ink); border-bottom-color: var(--brass); font-weight: 600; } .teacher-layout { display: grid; grid-template-columns: 260px 1fr; gap: 2rem; align-items: start; margin-top: 1rem; } .topics-panel, .quizzes-panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.25rem; } .topics-panel form { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-top: 0.75rem; } .topic-item { padding: 0.5rem 0.6rem; border-radius: var(--radius); cursor: pointer; font-size: 0.92rem; } .topic-item:hover { background: var(--brass-soft); } .topic-item.selected { background: var(--ink); color: var(--surface); } /* Outline buttons default to ink-on-transparent, which is invisible against the dark background above — flip them to light-on-transparent here. */ .topic-item.selected button[type="button"] { color: var(--surface); border-color: var(--surface); } .topic-item.selected button[type="button"]:hover:not(:disabled) { background: rgba(251, 250, 246, 0.15); } /* ---- Tests page: page-level header + quiz card grid ---- */ .page-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.5rem; } .page-header h1 { margin: 0; } .quiz-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 1rem; } /* Named `-manage-` to avoid colliding with the unrelated `.quiz-card` class used by the student-facing Browse page (Quizzes/Browse/View.fs). */ .quiz-manage-card { display: flex; flex-direction: column; gap: 0.6rem; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 1rem 1.25rem; cursor: pointer; } .quiz-manage-card:hover { border-color: var(--brass); } .quiz-manage-card.selected { background: var(--ink); color: var(--surface); border-color: var(--ink); } .quiz-manage-card-title { margin: 0; } .quiz-manage-card-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: auto; padding-top: 0.4rem; } .quiz-manage-card-actions button { flex: 1 1 auto; } /* Outline buttons default to ink-on-transparent, which is invisible against the dark background of a selected card — flip them to light-on-transparent. */ .quiz-manage-card.selected button[type="button"] { color: var(--surface); border-color: var(--surface); } .quiz-manage-card.selected button[type="button"]:hover:not(:disabled) { background: rgba(251, 250, 246, 0.15); } /* ---- Tests page: results view (quiz cards shift into a narrow left column, results table takes the rest) ---- */ .results-layout { display: grid; grid-template-columns: 320px 1fr; gap: 2rem; align-items: start; } .results-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; } .results-table th, .results-table td { text-align: left; padding: 0.55rem 0.75rem; border-bottom: 1px solid var(--line); } .results-table th { font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-soft); } .results-table tbody tr:hover { background: var(--brass-soft); } .focus-loss-value { font-family: var(--font-mono); } .focus-loss-value.warn { color: var(--fail); font-weight: 600; } /* Compact, non-animated variant for the "Результат" column — the default `.grade-stamp` is sized/rotated for the standalone result screen and would overflow a table cell. */ .results-table .grade-stamp { font-size: 0.75rem; border-width: 1px; padding: 0.15rem 0.5rem; margin: 0; transform: none; animation: none; } .questions-list, .assignment-panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.25rem 1.5rem; margin-bottom: 1.25rem; } .questions-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; border-top: 1px solid var(--line); padding: 0.6rem 0; } .questions-list li:first-child { border-top: none; } .question-kind { font-family: var(--font-mono); font-size: 0.75rem; color: var(--brass); text-transform: uppercase; letter-spacing: 0.03em; margin-right: 0.5rem; } .new-question-form { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.25rem 1.5rem; } .new-question-form > button + button { margin-left: 0.5rem; } .pool-picker { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; } .pool-picker p { flex-basis: 100%; margin-bottom: 0; } .question-picker input[type="text"] + button { margin-left: 0.5rem; } /* The global `form input:not([type=radio]):not([type=checkbox]) { width: 100% }` rule outweighs a single class here, so this needs !important to keep the field beside its button instead of stacking under it. */ .pool-count-input { display: inline-block !important; width: 6rem !important; } .pool-rules-summary li { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.3rem 0; } /* ---- Modal overlay (quiz create/edit form) ---- */ .modal-backdrop { position: fixed; inset: 0; background: rgba(29, 35, 51, 0.55); display: flex; justify-content: center; align-items: flex-start; padding: 3rem 1.5rem; overflow-y: auto; z-index: 100; } .modal-dialog { width: 100%; max-width: 560px; } .modal-backdrop .new-question-form { box-shadow: 0 20px 48px rgba(29, 35, 51, 0.3); } .options-editor { margin: 0.5rem 0 1rem; } .option-row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; } .option-row input[type="text"] { flex: 1; } .student-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.4rem 0; font-size: 0.92rem; cursor: pointer; } .student-row input { width: auto; } /* ---- Admin: users list ---- */ .users-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; border-top: 1px solid var(--line); padding: 0.6rem 0; } .users-list li:first-child { border-top: none; } .user-row-info { display: flex; flex-direction: column; gap: 0.1rem; flex: 1 1 200px; } .user-row-name { font-weight: 500; } .user-row-badges { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.3rem; } .user-row-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-left: auto; } .status-badge { font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.04em; text-transform: uppercase; border-radius: var(--radius); padding: 0.15rem 0.5rem; } .status-badge.active { color: var(--pass); background: var(--pass-bg); } .status-badge.inactive { color: var(--fail); background: var(--fail-bg); } /* ============================================================ Accessibility & motion ============================================================ */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; } } /* ============================================================ Responsive ============================================================ */ @media (max-width: 640px) { .teacher-layout, .results-layout { grid-template-columns: 1fr; } .topbar { padding: 0.75rem 1rem; } .quiz-list-page, .taking-quiz-page, .result-page, .teacher-home { padding: 1.25rem 1rem 3rem; } .quiz-actions { margin: 0 -1rem; padding: 1rem; } }