diff --git a/frontend/src/app.css b/frontend/src/app.css index c063aaa..573bf14 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -15,8 +15,8 @@ --color-primary-dark: #0a58ca; --color-bg: #ffffff; --color-bg-secondary: #f8f9fa; - --color-text: #212529; - --color-text-muted: #6c757d; + --color-text: #111111; /* Near black for maximum contrast */ + --color-text-muted: #444444; --color-border: #dee2e6; --color-success: #198754; --color-danger: #dc3545; @@ -77,10 +77,15 @@ } html { - font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; + font-family: 'Montserrat', 'Roboto', system-ui, -apple-system, 'Segoe UI', Arial, sans-serif; color: var(--color-text); background: var(--color-bg); - line-height: 1.6; + font-size: 16px; + font-weight: 500; /* Medium weight */ + line-height: 1.5; + text-align: left; + -webkit-font-smoothing: auto; + -moz-osx-font-smoothing: auto; } body { @@ -143,6 +148,20 @@ pre code { font-size: 0.85rem; } +/* ── Blockquotes (markdown rendered) ─────────────────────────── */ +blockquote { + border-left: 4px solid var(--color-primary); + background: var(--color-bg-secondary); + padding: 0.5rem 1rem; + margin-bottom: 1rem; + border-radius: 0 var(--radius) var(--radius) 0; + font-style: italic; + color: var(--color-text-muted); +} +blockquote p { + margin-bottom: 0 !important; +} + /* ── Responsive images ───────────────────────────────────────── */ img { max-width: 100%; diff --git a/frontend/src/app.html b/frontend/src/app.html index a1c50e5..0c79107 100644 --- a/frontend/src/app.html +++ b/frontend/src/app.html @@ -7,6 +7,9 @@ + + + %sveltekit.head% diff --git a/frontend/src/routes/lesson/[slug]/lesson.css b/frontend/src/routes/lesson/[slug]/lesson.css index 88107cb..ab02e68 100644 --- a/frontend/src/routes/lesson/[slug]/lesson.css +++ b/frontend/src/routes/lesson/[slug]/lesson.css @@ -1,14 +1,40 @@ /* ── Shared rich-text styles (.prose + .tab-content) ──── */ +.prose, .tab-content { - font-size: 0.85rem; - padding: 0.75rem 0.5rem; - line-height: 1.65; + font-size: 1rem; /* 16px */ + line-height: 1.5; + text-align: left; + max-width: 70ch; /* Limit line length for readability */ } + +.tab-content { + padding: 1rem 0.5rem; +} + .tab-heading { color: var(--color-primary); - font-size: 1.1rem; + font-size: 1.5rem; /* 24px */ margin-top: 0; + margin-bottom: 1rem; } + +.prose h2, .tab-content h2 { + font-size: 1.5rem; /* 24px */ + margin-top: 3rem; + margin-bottom: 1.5rem; +} + +.prose h3, .tab-content h3 { + font-size: 1.25rem; /* 20px */ + margin-top: 2rem; + margin-bottom: 1rem; +} + +.prose p, +.tab-content p { + margin-bottom: 1.5rem; +} + .prose pre, .tab-content pre { background: var(--color-bg-secondary); @@ -17,48 +43,6 @@ overflow-x: auto; position: relative; } - -.btn-try { - position: absolute; - top: 0.5rem; - right: 0.5rem; - opacity: 0; - transition: opacity 0.2s ease-in-out; - font-size: 0.7rem; - padding: 0.2rem 0.5rem; - border-radius: var(--radius); - background-color: var(--color-primary); - color: #fff; - border: none; - cursor: pointer; - z-index: 10; -} - -.prose pre:hover .btn-try, -.tab-content pre:hover .btn-try { - opacity: 1; -} - -@media (max-width: 768px) { - .btn-try { - opacity: 1; - } -} -.prose code, -.tab-content code { - font-family: var(--font-mono); - font-size: 0.85rem; -} -.prose p, -.tab-content p { - margin-bottom: 0.75rem; -} - -.prose h2, .prose h3, -.tab-content h2, .tab-content h3 { - margin-top: 1.25rem; - margin-bottom: 0.5rem; -} .tab-content ul, .tab-content ol { margin-bottom: 0.75rem; diff --git a/services/lesson_service.py b/services/lesson_service.py index 9302e26..6a99b92 100644 --- a/services/lesson_service.py +++ b/services/lesson_service.py @@ -351,7 +351,7 @@ def _parse_flashcards(text): body = re.sub(r'^\s*image:\s*.*$', '', body, flags=re.MULTILINE).strip() # Check for explanation (blockquote starting with >) - explanation_match = re.search(r'^\s*>\s*(.*)$', body, re.MULTILINE | re.DOTALL) + explanation_match = re.search(r'^\s*(>.*)$', body, re.MULTILINE | re.DOTALL) explanation = explanation_match.group(1).strip() if explanation_match else "" # If MCQ options exist, it's an MCQ. We also remove the options from the body to find clean explanation.