diff --git a/website/css/style.css b/website/css/style.css index 67ffb32..bb9ebe9 100644 --- a/website/css/style.css +++ b/website/css/style.css @@ -1,10 +1,11 @@ :root { color-scheme: dark; --bg: #0f1218; - --surface: #1a1f2a; - --surface-raised: #222836; - --surface-hover: #2a3140; - --surface-muted: #161b24; + --surface: rgba(26, 31, 42, 0.94); + --surface-raised: rgba(34, 40, 54, 0.9); + --surface-hover: rgba(42, 49, 64, 0.92); + --surface-muted: rgba(22, 27, 36, 0.82); + --surface-input: rgba(34, 40, 54, 0.88); --primary: #3b82f6; --primary-hover: #60a5fa; --primary-subtle: rgba(59, 130, 246, 0.14); @@ -24,14 +25,24 @@ --text-on-primary: #fff; --border: #2e3648; --radius: 8px; + --radius-lg: 12px; --shadow: 0 1px 3px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.25); - --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.45); + --shadow-lg: 0 4px 24px rgba(0, 0, 0, 0.4); + --shadow-inset: inset 0 1px 0 rgba(255, 255, 255, 0.04); --focus-ring: rgba(59, 130, 246, 0.28); --sidebar-width: 240px; --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; - --table-header-bg: #1e2430; - --row-hover-bg: #252c3a; + --table-header-bg: rgba(30, 36, 48, 0.97); + --table-stripe-bg: rgba(30, 36, 48, 0.45); + --row-hover-bg: rgba(37, 44, 58, 0.85); --sticky-shadow: rgba(0, 0, 0, 0.35); + --atmosphere-blue: rgba(59, 130, 246, 0.11); + --atmosphere-indigo: rgba(99, 102, 241, 0.07); + --grid-line: rgba(148, 163, 184, 0.035); + --accent-gradient: linear-gradient(135deg, #3b82f6 0%, #4f6ef7 45%, #6366f1 100%); + --accent-gradient-soft: linear-gradient(90deg, rgba(59, 130, 246, 0.5), rgba(99, 102, 241, 0.35)); + --accent-glow: 0 4px 20px rgba(59, 130, 246, 0.22); + --shine-line: linear-gradient(90deg, transparent, rgba(148, 163, 184, 0.12), transparent); /* Incomplete translation highlight */ --trans-missing-bg: #1c222d; --trans-missing-bg-hover: #242b38; @@ -121,12 +132,40 @@ code { .layout { display: flex; min-height: 100vh; + position: relative; + isolation: isolate; +} + +/* Ambient backdrop (logged-in app shell — matches login atmosphere) */ +body.logged-in:not(.login-active) .layout::before { + content: ''; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + background: + radial-gradient(ellipse 85% 65% at 18% 22%, var(--atmosphere-blue), transparent 62%), + radial-gradient(ellipse 60% 50% at 82% 78%, var(--atmosphere-indigo), transparent 58%), + var(--bg); +} +body.logged-in:not(.login-active) .layout::after { + content: ''; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + background-image: + linear-gradient(var(--grid-line) 1px, transparent 1px), + linear-gradient(90deg, var(--grid-line) 1px, transparent 1px); + background-size: 56px 56px; + mask-image: radial-gradient(ellipse 100% 90% at 55% 40%, #000 15%, transparent 72%); } /* Sidebar */ .sidebar { width: var(--sidebar-width); - background: var(--surface); + background: color-mix(in srgb, var(--surface) 88%, transparent); + backdrop-filter: blur(14px); border-right: 1px solid var(--border); display: flex; flex-direction: column; @@ -135,12 +174,28 @@ code { left: 0; bottom: 0; z-index: 100; + box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15); } .sidebar-brand { - padding: 20px; + position: relative; + padding: 20px 20px 20px 24px; border-bottom: 1px solid var(--border); font-weight: 700; - font-size: 1.1rem; + font-size: 1.05rem; + line-height: 1.35; + letter-spacing: -0.02em; + background: linear-gradient(180deg, rgba(59, 130, 246, 0.08) 0%, transparent 100%); +} +.sidebar-brand::before { + content: ''; + position: absolute; + left: 0; + top: 18px; + bottom: 18px; + width: 3px; + border-radius: 0 2px 2px 0; + background: var(--accent-gradient); + box-shadow: 0 0 12px rgba(59, 130, 246, 0.45); } .sidebar-brand small { display: block; @@ -158,25 +213,40 @@ code { align-items: center; gap: 10px; padding: 10px 20px; + margin: 0 8px; + border-radius: 6px; color: var(--text-secondary); text-decoration: none; font-size: .9rem; - transition: background .15s, color .15s; + transition: background .15s, color .15s, box-shadow .15s; } -.sidebar-nav a:hover, -.sidebar-nav a.active { +.sidebar-nav a:hover { background: var(--surface-hover); - color: var(--primary); + color: var(--text); +} +.sidebar-nav a.active { + background: var(--primary-subtle); + color: var(--primary-text); + box-shadow: inset 3px 0 0 var(--primary); + border-radius: 0 6px 6px 0; + margin-left: 0; + padding-left: 17px; } .sidebar-nav a svg { width: 18px; height: 18px; flex-shrink: 0; + transition: color 0.15s, filter 0.15s; +} +.sidebar-nav a.active svg { + color: var(--primary); + filter: drop-shadow(0 0 5px rgba(59, 130, 246, 0.35)); } .sidebar-footer { padding: 16px 20px; border-top: 1px solid var(--border); font-size: .85rem; + background: rgba(15, 18, 24, 0.35); } .sidebar-footer .user-info { color: var(--text-secondary); @@ -185,6 +255,8 @@ code { /* Main content */ .main-content { + position: relative; + z-index: 1; margin-left: var(--sidebar-width); flex: 1; padding: 28px 32px; @@ -193,11 +265,103 @@ code { /* Cards */ .card { + position: relative; + overflow: hidden; background: var(--surface); border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg), var(--shadow-inset); padding: 24px; + transition: border-color 0.2s, box-shadow 0.2s; +} +body.logged-in .card::before { + content: ''; + position: absolute; + inset: 0 0 auto 0; + height: 1px; + background: var(--shine-line); + pointer-events: none; +} +body.logged-in .card > h3 { + margin: 0 0 14px; + font-size: 1rem; + font-weight: 600; + letter-spacing: -0.01em; + padding-bottom: 10px; + border-bottom: 1px solid var(--border); + background: linear-gradient(90deg, var(--text) 0%, var(--text-secondary) 120%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +/* Elevated panels & inputs (login visual language) */ +body.logged-in :is( + .question-item, + .inline-form-card, + .trans-lang-panel, + .trans-top-bar, + .trans-sheet, + .lang-manager, + .condition-editor, + .scoring-profile-card, + .scoring-profile-picker, + .coach-client-group, + .client-qn-panel, + .table-wrapper, + .trans-table-wrapper, + .trans-list, + .pending-option-item, + .translations-panel, + .coach-recent-panel, + .client-detail-panel, + .option-item +) { + border-radius: var(--radius-lg); + box-shadow: var(--shadow), var(--shadow-inset); +} + +body.logged-in .table-wrapper, +body.logged-in .trans-table-wrapper { + border: 1px solid var(--border); + background: var(--surface); +} + +body.logged-in :is( + .category-german-input, + .coach-supervisor-select, + .type-select, + .trans-select, + .lang-add-code, + .lang-add-name, + .trans-search-input, + .followup-note-input, + .condition-json-fallback, + .scoring-profile-picker input, + .scoring-profile-picker select, + .value-score-pts, + .translation-row input[type="text"], + .trans-list-item .trans-cell-input +) { + background: var(--surface-input); + border-radius: var(--radius); +} + +body.logged-in .btn-home { + border-radius: var(--radius); + background: var(--surface-input); + box-shadow: var(--shadow); +} +body.logged-in .btn-home:hover { + background: var(--surface-hover); + border-color: var(--primary-border); + box-shadow: var(--accent-glow); +} +body.logged-in .btn-home svg { + transition: color 0.15s; +} +body.logged-in .btn-home:hover svg { + color: var(--primary-text); } /* Buttons */ @@ -209,8 +373,8 @@ code { font-size: .875rem; font-weight: 500; border: 1px solid var(--border); - border-radius: 6px; - background: var(--surface); + border-radius: var(--radius); + background: var(--surface-input); color: var(--text); cursor: pointer; transition: background .15s, box-shadow .15s; @@ -222,14 +386,15 @@ code { box-shadow: var(--shadow); } .btn-primary { - background: var(--primary); + background: var(--accent-gradient); color: var(--text-on-primary); - border-color: var(--primary); + border-color: rgba(99, 102, 241, 0.5); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.1); } .btn-primary:hover { - background: var(--primary-hover); - border-color: var(--primary-hover); - box-shadow: var(--shadow); + background: linear-gradient(135deg, #60a5fa 0%, #6d8ef8 45%, #818cf8 100%); + border-color: rgba(129, 140, 248, 0.6); + box-shadow: var(--accent-glow), inset 0 1px 0 rgba(255, 255, 255, 0.14); } .btn-danger { background: var(--danger-subtle); @@ -283,14 +448,14 @@ code { .form-group select, .form-group textarea { width: 100%; - padding: 8px 12px; + padding: 10px 12px; border: 1px solid var(--border); - border-radius: 6px; + border-radius: var(--radius); font-size: .9rem; font-family: var(--font); - background: var(--surface); + background: var(--surface-input); color: var(--text); - transition: border-color .15s; + transition: border-color .15s, box-shadow .15s; } .form-group input:focus, .form-group select:focus, @@ -314,6 +479,7 @@ code { border-radius: 999px; text-transform: uppercase; letter-spacing: .03em; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); } .badge-draft { background: var(--badge-draft-bg); color: var(--badge-draft-fg); } .badge-active { background: var(--badge-active-bg); color: var(--badge-active-fg); } @@ -461,7 +627,7 @@ table.data-table.activity-log-table .badge { display: flex; align-items: center; justify-content: space-between; - margin-bottom: 24px; + margin-bottom: 28px; flex-wrap: wrap; gap: 12px; } @@ -474,10 +640,26 @@ table.data-table.activity-log-table .badge { .page-header-titles { min-width: 0; } +body.logged-in .page-header-titles { + position: relative; + padding-left: 14px; +} +body.logged-in .page-header-titles::before { + content: ''; + position: absolute; + left: 0; + top: 3px; + bottom: 3px; + width: 3px; + border-radius: 2px; + background: var(--accent-gradient); + box-shadow: 0 0 10px rgba(59, 130, 246, 0.35); +} .page-header h1 { font-size: 1.5rem; font-weight: 700; margin: 0; + letter-spacing: -0.02em; } .page-header .actions { display: flex; @@ -525,10 +707,10 @@ table.data-table.activity-log-table .badge { max-width: 100%; min-width: 160px; font-size: 0.85rem; - padding: 6px 8px; + padding: 8px 10px; border: 1px solid var(--border); - border-radius: 6px; - background: var(--surface); + border-radius: var(--radius); + background: var(--surface-input); color: var(--text); } @@ -559,9 +741,21 @@ table.data-table.activity-log-table .badge { font-size: 1.05rem; font-weight: 600; color: var(--text-secondary); - letter-spacing: .02em; + letter-spacing: -0.01em; padding-bottom: 4px; + padding-left: 12px; border-bottom: 1px solid var(--border); + position: relative; +} +.q-category-title::before { + content: ''; + position: absolute; + left: 0; + top: 0.15em; + width: 4px; + height: 1em; + border-radius: 2px; + background: var(--accent-gradient-soft); } .q-category-title .category-title-input { @@ -627,17 +821,32 @@ table.data-table.activity-log-table .badge { color: var(--text-secondary); } .q-card { + position: relative; + overflow: hidden; background: var(--surface); border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); padding: 20px; - box-shadow: var(--shadow); - transition: box-shadow .2s, transform .2s; + box-shadow: var(--shadow), var(--shadow-inset); + transition: box-shadow .2s, transform .2s, border-color .2s; cursor: pointer; } +.q-card::after { + content: ''; + position: absolute; + inset: 0 0 auto 0; + height: 2px; + background: var(--accent-gradient-soft); + opacity: 0; + transition: opacity 0.2s; +} .q-card:hover { - box-shadow: var(--shadow-lg); - transform: translateY(-2px); + box-shadow: var(--shadow-lg), var(--accent-glow); + border-color: color-mix(in srgb, var(--primary-border) 60%, var(--border)); + transform: translateY(-3px); +} +.q-card:hover::after { + opacity: 1; } .q-card-header { display: flex; @@ -693,14 +902,29 @@ table.data-table th { z-index: 2; box-shadow: 0 1px 0 var(--border); } +table.data-table tbody tr { + transition: background 0.12s ease; +} +table.data-table tbody tr:nth-child(even) td { + background: var(--table-stripe-bg); +} +table.data-table tbody tr:nth-child(even) td.sticky-col { + background: color-mix(in srgb, var(--table-stripe-bg) 70%, var(--surface)); +} table.data-table tr:hover td { background: var(--row-hover-bg); } +table.data-table tbody tr:hover td.sticky-col { + background: color-mix(in srgb, var(--row-hover-bg) 85%, var(--surface)); +} +table.data-table tbody tr:hover td:first-child:not(.sticky-col) { + box-shadow: inset 3px 0 0 var(--primary); +} table.data-table .sticky-col { position: sticky; left: 0; z-index: 1; - background: var(--surface); + background: color-mix(in srgb, var(--surface) 98%, var(--bg)); box-shadow: 2px 0 4px var(--sticky-shadow); } table.data-table thead th.sticky-col { @@ -719,9 +943,10 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } } .question-item { border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); margin-bottom: 8px; background: var(--surface); + box-shadow: var(--shadow), var(--shadow-inset); } .question-header { display: flex; @@ -754,9 +979,16 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } display: none; padding: 0 16px 16px 42px; } +.question-item.open { + border-color: color-mix(in srgb, var(--primary-border) 45%, var(--border)); + box-shadow: var(--shadow), 0 0 0 1px color-mix(in srgb, var(--primary-border) 30%, transparent); +} .question-item.open .question-body { display: block; } +.question-item.open .question-header { + background: color-mix(in srgb, var(--primary-subtle) 40%, transparent); +} .question-item.open .chevron { transform: rotate(90deg); } @@ -797,8 +1029,9 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } margin-top: 12px; padding: 12px; background: var(--surface-muted); - border-radius: 6px; + border-radius: var(--radius-lg); border: 1px solid var(--border); + box-shadow: var(--shadow), var(--shadow-inset); } .translations-panel h4 { font-size: .85rem; @@ -845,31 +1078,168 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } } /* Login */ -.login-wrapper { +body.login-active .main-content { + margin-left: 0; + padding: 0; + min-height: 100vh; +} + +.login-screen { + position: relative; display: flex; align-items: center; justify-content: center; min-height: 100vh; - padding: 20px; + padding: 28px 32px; + overflow: hidden; + isolation: isolate; } -.login-card { + +.login-screen__backdrop { + position: absolute; + inset: 0; + z-index: 0; + background: + radial-gradient(ellipse 80% 60% at 30% 40%, rgba(59, 130, 246, 0.16), transparent 65%), + radial-gradient(ellipse 55% 45% at 75% 65%, rgba(99, 102, 241, 0.1), transparent 55%), + var(--bg); +} + +.login-grid { + position: absolute; + inset: 0; + background-image: + linear-gradient(var(--grid-line) 1px, transparent 1px), + linear-gradient(90deg, var(--grid-line) 1px, transparent 1px); + background-size: 56px 56px; + mask-image: radial-gradient(ellipse 90% 80% at 50% 45%, #000 25%, transparent 80%); +} + +.login-orb { + position: absolute; + border-radius: 50%; + filter: blur(72px); + opacity: 0.45; + animation: login-orb-drift 22s ease-in-out infinite; +} +.login-orb--a { + width: 420px; + height: 420px; + background: rgba(59, 130, 246, 0.26); + top: 10%; + left: 15%; +} +.login-orb--b { + width: 300px; + height: 300px; + background: rgba(99, 102, 241, 0.16); + bottom: 10%; + right: 20%; + animation-delay: -9s; +} + +@keyframes login-orb-drift { + 0%, 100% { transform: translate(0, 0); } + 50% { transform: translate(18px, -12px); } +} + +.login-screen__content { + position: relative; + z-index: 1; width: 100%; max-width: 400px; } -.login-card h1 { font-size: 1.5rem; margin-bottom: 4px; } -.login-card .subtitle { - color: var(--text-secondary); - font-size: .9rem; - margin-bottom: 24px; + +.login-brand { + margin-bottom: 28px; } -.login-card .login-privacy-note { - color: var(--text-secondary); - font-size: .8rem; - line-height: 1.4; - margin: -12px 0 20px; + +.login-brand__title { + font-size: 1.5rem; + font-weight: 700; + line-height: 1.25; + letter-spacing: -0.02em; + margin-bottom: 6px; } + +.login-brand__subtitle { + font-size: 0.875rem; + color: var(--text-secondary); +} + +.login-card { + position: relative; + overflow: hidden; + box-shadow: var(--shadow-lg), var(--accent-glow), var(--shadow-inset); +} +.login-card::before { + content: ''; + position: absolute; + inset: 0 0 auto 0; + height: 2px; + background: var(--accent-gradient); + opacity: 0.85; +} + +.login-card__heading { + font-size: 1.15rem; + font-weight: 700; + margin-bottom: 4px; +} + +.login-card__lead { + font-size: 0.875rem; + color: var(--text-secondary); + margin-bottom: 20px; +} + +.login-form .form-group { + margin-bottom: 16px; +} + +.login-alert { + margin-top: 12px; + padding: 10px 12px; + border-radius: var(--radius); + font-size: 0.85rem; + line-height: 1.45; +} +.login-alert--error { + color: var(--danger-text); + background: var(--danger-subtle); + border: 1px solid var(--danger-border); +} + +.login-card__footnote { + margin-top: 20px; + padding-top: 16px; + border-top: 1px solid var(--border); + font-size: 0.8rem; + color: var(--text-secondary); +} + .error-text { color: var(--danger); font-size: .85rem; margin-top: 8px; } +@media (max-width: 480px) { + .login-screen { + padding: 20px 16px; + align-items: flex-start; + padding-top: 48px; + } +} + +@media (prefers-reduced-motion: reduce) { + .login-orb, + .spinner { + animation: none; + } + .q-card:hover, + .home-kpi-link:hover, + .home-link-card:hover { + transform: none; + } +} + /* Toast */ #toastContainer { position: fixed; @@ -882,11 +1252,11 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } } .toast { padding: 12px 16px 12px 14px; - border-radius: 8px; + border-radius: var(--radius-lg); font-size: .875rem; font-weight: 500; line-height: 1.45; - box-shadow: var(--shadow-lg); + box-shadow: var(--shadow-lg), 0 8px 24px rgba(0, 0, 0, 0.35); opacity: 0; transform: translateX(40px); transition: opacity .3s, transform .3s; @@ -926,11 +1296,12 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } } .filter-bar select, .filter-bar input { - padding: 6px 10px; + padding: 8px 10px; border: 1px solid var(--border); - border-radius: 6px; + border-radius: var(--radius); font-size: .85rem; - background: var(--surface); + background: var(--surface-input); + color: var(--text); } .filter-bar input[type="search"], .filter-bar input[type="text"].filter-search { @@ -972,10 +1343,17 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } /* Empty state */ .empty-state { text-align: center; - padding: 60px 20px; + padding: 56px 24px; color: var(--text-secondary); + border: 1px dashed color-mix(in srgb, var(--border) 80%, var(--primary-border)); + border-radius: var(--radius-lg); + background: color-mix(in srgb, var(--surface-muted) 70%, transparent); +} +.empty-state svg { + margin-bottom: 16px; + opacity: .5; + color: var(--primary-text); } -.empty-state svg { margin-bottom: 16px; opacity: .4; } .empty-state h3 { margin-bottom: 8px; color: var(--text); } /* Dragging */ @@ -991,12 +1369,21 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } /* Loading spinner */ .spinner { display: inline-block; - width: 20px; - height: 20px; + width: 26px; + height: 26px; border: 2px solid var(--border); border-top-color: var(--primary); + border-right-color: #6366f1; border-radius: 50%; - animation: spin .6s linear infinite; + animation: spin .7s linear infinite; + box-shadow: 0 0 12px rgba(59, 130, 246, 0.15); +} +#app > .spinner, +.main-content > .spinner, +[id$="Content"] > .spinner:only-child, +[id$="Root"] > .spinner:only-child { + display: block; + margin: 56px auto; } @keyframes spin { to { transform: rotate(360deg); } } @@ -1098,7 +1485,7 @@ table.data-table tbody tr.row-orphan-category td { flex-shrink: 0; padding: 14px 16px; border-bottom: 1px solid var(--border); - background: var(--surface); + background: rgba(34, 40, 54, 0.5); } .assign-panel-header h3 { @@ -1303,34 +1690,43 @@ table.data-table tbody tr.row-orphan-category td { padding: 8px 12px; background: var(--primary-subtle); border: 1px solid var(--primary-border); - border-radius: 6px; + border-radius: var(--radius); font-size: .875rem; color: var(--primary-text); + box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.05); } /* Tabs */ .tab-bar { display: flex; - gap: 0; - border-bottom: 2px solid var(--border); + gap: 4px; + padding: 4px; + border-bottom: none; margin-bottom: 20px; + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-inset); } .tab-bar button { - padding: 10px 20px; + padding: 9px 18px; font-size: .9rem; font-weight: 500; - background: none; + background: transparent; border: none; - border-bottom: 2px solid transparent; - margin-bottom: -2px; + border-radius: var(--radius); cursor: pointer; color: var(--text-secondary); - transition: color .15s, border-color .15s; + transition: color .15s, background .15s, box-shadow .15s; +} +.tab-bar button:hover { + color: var(--text); + background: var(--surface-hover); } -.tab-bar button:hover { color: var(--text); } .tab-bar button.active { - color: var(--primary); - border-bottom-color: var(--primary); + color: var(--primary-text); + background: var(--primary-subtle); + box-shadow: inset 0 0 0 1px var(--primary-border), 0 1px 4px rgba(59, 130, 246, 0.12); } /* Metadata form in editor */ @@ -1348,9 +1744,10 @@ table.data-table tbody tr.row-orphan-category td { .inline-form-card { background: var(--surface-muted); border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); padding: 16px; margin-bottom: 12px; + box-shadow: var(--shadow), var(--shadow-inset); } .inline-form-card h4 { font-size: .9rem; @@ -1362,12 +1759,12 @@ table.data-table tbody tr.row-orphan-category td { /* Type select */ .type-select { width: 100%; - padding: 8px 12px; + padding: 10px 12px; border: 1px solid var(--border); - border-radius: 6px; + border-radius: var(--radius); font-size: .9rem; font-family: var(--font); - background: var(--surface); + background: var(--surface-input); color: var(--text); } .type-select:focus { @@ -1401,7 +1798,10 @@ table.data-table tbody tr.row-orphan-category td { align-items: center; justify-content: center; padding: 16px; - background: color-mix(in srgb, var(--bg) 30%, rgba(0, 0, 0, 0.65)); + background: + radial-gradient(ellipse 70% 50% at 50% 40%, rgba(59, 130, 246, 0.08), transparent 70%), + color-mix(in srgb, var(--bg) 25%, rgba(0, 0, 0, 0.7)); + backdrop-filter: blur(4px); } .modal-overlay[hidden] { display: none; @@ -1414,10 +1814,10 @@ body.modal-open { max-width: 460px; max-height: calc(100vh - 32px); overflow: auto; - background: var(--surface); + background: color-mix(in srgb, var(--surface) 98%, var(--bg)); border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-lg); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg), var(--shadow-inset); padding: 24px; } .modal-dialog h2 { @@ -1460,7 +1860,7 @@ body.modal-open { display: block; padding: 12px 14px; border: 1px solid var(--border); - border-radius: 8px; + border-radius: var(--radius); cursor: pointer; transition: border-color 0.15s, background 0.15s; } @@ -1496,7 +1896,7 @@ body.modal-open { line-height: 1.45; padding: 10px 12px; background: var(--surface-muted); - border-radius: 6px; + border-radius: var(--radius); border: 1px solid var(--border); } @@ -1613,7 +2013,7 @@ body.modal-open { padding: 12px 14px; background: var(--surface-muted); border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); } .trans-lang-panel summary { cursor: pointer; @@ -1629,7 +2029,7 @@ body.modal-open { .required-mark { color: var(--danger); font-weight: 600; } .trans-sheet { border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); background: var(--surface); display: flex; flex-direction: column; @@ -1676,7 +2076,7 @@ body.modal-open { padding: 12px 14px; background: var(--surface-muted); border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); } .trans-top-stats { display: flex; @@ -1796,7 +2196,7 @@ body.modal-open { } .trans-list { border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); overflow: hidden; } .trans-list-item { @@ -1833,6 +2233,9 @@ body.modal-open { .trans-sheet-body > .trans-groups-flat .trans-group-details:last-child .trans-list-item:last-child { border-bottom: none; } +.trans-list-item:nth-child(even) { + background: var(--table-stripe-bg); +} .trans-list-item:hover { background: var(--row-hover-bg); } .trans-list-item-missing { background: var(--trans-missing-bg); @@ -1916,7 +2319,7 @@ body.modal-open { .lang-manager { background: var(--surface-muted); border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); padding: 14px 16px; margin-bottom: 20px; } @@ -2000,7 +2403,7 @@ body.modal-open { .trans-table-wrapper { overflow-x: auto; border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); } .trans-table { width: 100%; @@ -2020,6 +2423,9 @@ body.modal-open { } .trans-table td { padding: 2px 4px; } .trans-table tbody tr { border-bottom: 1px solid var(--border); } +.trans-table tbody tr:nth-child(even) { + background: var(--table-stripe-bg); +} .trans-table tbody tr:hover { background: var(--row-hover-bg); } .trans-row-hidden { display: none !important; } #tabContent-translations .trans-list, @@ -2177,11 +2583,25 @@ body.modal-open { .home-kpi-link { text-decoration: none; color: inherit; - transition: border-color 0.15s, background 0.15s; + position: relative; + overflow: hidden; + transition: border-color 0.2s, background 0.2s, transform 0.2s, box-shadow 0.2s; +} +.home-kpi-link::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: var(--accent-gradient); + opacity: 0.75; } .home-kpi-link:hover { border-color: var(--primary-border); background: var(--surface-hover); + transform: translateY(-3px); + box-shadow: var(--shadow-lg), var(--accent-glow); } .home-dashboard-grid { display: grid; @@ -2196,17 +2616,36 @@ body.modal-open { } .home-link-card { display: block; - padding: 14px 16px; + position: relative; + padding: 14px 36px 14px 16px; border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); background: var(--surface-muted); text-decoration: none; color: inherit; - transition: border-color 0.15s, background 0.15s; + transition: border-color 0.2s, background 0.2s, box-shadow 0.2s, transform 0.2s; + box-shadow: var(--shadow), var(--shadow-inset); +} +.home-link-card::after { + content: '→'; + position: absolute; + right: 14px; + top: 50%; + transform: translateY(-50%); + font-size: 1rem; + color: var(--primary-text); + opacity: 0.35; + transition: transform 0.2s, opacity 0.2s; } .home-link-card:hover { border-color: var(--primary-border); background: var(--surface-hover); + box-shadow: var(--shadow-lg), 0 0 0 1px var(--primary-border); + transform: translateX(2px); +} +.home-link-card:hover::after { + opacity: 1; + transform: translateY(-50%) translateX(3px); } .home-link-title { display: block; @@ -2227,8 +2666,12 @@ body.modal-open { .insights-kpi-value { font-size: 2rem; font-weight: 700; - color: var(--text); line-height: 1.2; + letter-spacing: -0.03em; + background: linear-gradient(160deg, var(--text) 20%, var(--primary-text) 100%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; } .insights-kpi-label { font-size: 0.85rem; @@ -2244,6 +2687,14 @@ body.modal-open { .insights-chart-card h3 { margin: 0 0 4px; font-size: 1rem; + letter-spacing: -0.01em; +} +body.logged-in .insights-chart-card.card { + transition: border-color 0.2s, box-shadow 0.2s; +} +body.logged-in .insights-chart-card.card:hover { + border-color: var(--primary-border); + box-shadow: var(--shadow-lg), 0 0 20px rgba(59, 130, 246, 0.08); } .insights-chart-hint { margin: 0 0 14px; @@ -2596,7 +3047,8 @@ select:disabled { padding: 16px; background: var(--surface-muted); border: 1px solid var(--border); - border-radius: 8px; + border-radius: var(--radius-lg); + box-shadow: var(--shadow), var(--shadow-inset); } .condition-mode-panel { margin-top: 12px; @@ -2973,10 +3425,11 @@ select:disabled { } .scoring-profile-card { border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); padding: 16px 18px; background: var(--surface-muted); color: var(--text); + box-shadow: var(--shadow), var(--shadow-inset); } .scoring-profile-card-top { display: flex; @@ -3052,9 +3505,10 @@ select:disabled { } .scoring-profile-picker { border: 1px solid var(--border); - border-radius: var(--radius); + border-radius: var(--radius-lg); padding: 12px; background: var(--surface-muted); + box-shadow: var(--shadow), var(--shadow-inset); } .scoring-profile-picker input, .scoring-profile-picker select { diff --git a/website/js/app.js b/website/js/app.js index f26e386..9290399 100644 --- a/website/js/app.js +++ b/website/js/app.js @@ -104,6 +104,8 @@ function updateNav() { const nav = document.getElementById('mainNav'); const userInfo = document.getElementById('userInfo'); document.body.classList.toggle('logged-in', isLoggedIn()); + const hash = window.location.hash.slice(1) || '/'; + document.body.classList.toggle('login-active', hash === '/login' || hash.startsWith('/login?')); if (isLoggedIn()) { nav.style.display = ''; const role = getRole(); @@ -117,7 +119,6 @@ function updateNav() { }); // Highlight active nav link - const hash = window.location.hash.slice(1) || '/'; document.querySelectorAll('.sidebar-nav a').forEach(a => { const href = a.getAttribute('href').slice(1); let active = hash === href; diff --git a/website/js/pages/login.js b/website/js/pages/login.js index 0d638fb..0197e1d 100644 --- a/website/js/pages/login.js +++ b/website/js/pages/login.js @@ -4,6 +4,19 @@ import { apiGet, invalidateSessionCheck } from '../api.js'; const WEBSITE_ROLES = ['admin', 'supervisor']; +function setFormBusy(formId, busy, busyLabel) { + const form = document.getElementById(formId); + if (!form) return; + const btn = form.querySelector('button[type="submit"]'); + if (!btn) return; + if (!btn.dataset.defaultLabel) { + btn.dataset.defaultLabel = btn.textContent.trim(); + } + btn.disabled = busy; + btn.textContent = busy ? busyLabel : btn.dataset.defaultLabel; + form.querySelectorAll('input').forEach((el) => { el.disabled = busy; }); +} + export async function loginPage() { if (isLoggedIn()) { try { @@ -25,38 +38,54 @@ export async function loginPage() { const app = document.getElementById('app'); app.innerHTML = ` -
-
-

BW Schützt - NAT-AS

-

Questionnaire Management

-

Counselor app stores interview answers encrypted on device; data shown here is loaded from the server after upload.

-
-
- - -
-
- - -
- -

-
-
-
-

You must change your password before continuing.

-
+
+
+
+
+
+
+ +
+
+

BW Schützt - NAT-AS

+

Questionnaire Management

+
+ +
+

Sign in

+

Admin or supervisor account.

+ +
- - + +
- - + +
- -

+ +

+ +
+
+

You must change your password before continuing.

+
+
+ + +
+
+ + +
+ +

+
+
+ +

Counselors use the mobile app.

@@ -68,10 +97,11 @@ export async function loginPage() { document.getElementById('loginForm').addEventListener('submit', async (e) => { e.preventDefault(); const errEl = document.getElementById('loginError'); - errEl.style.display = 'none'; + errEl.hidden = true; const username = document.getElementById('username').value.trim(); const password = document.getElementById('password').value; + setFormBusy('loginForm', true, 'Signing in…'); try { const res = await fetch('../api/auth/login', { method: 'POST', @@ -92,20 +122,23 @@ export async function loginPage() { } } errEl.textContent = msg; - errEl.style.display = ''; + errEl.hidden = false; return; } const d = json.data; if (d.role === 'coach') { errEl.textContent = 'Counselor accounts can only sign in through the mobile app.'; - errEl.style.display = ''; + errEl.hidden = false; return; } if (d.mustChangePassword) { pendingUsername = username; pendingTempToken = d.token; - document.getElementById('loginForm').style.display = 'none'; - document.getElementById('changePwSection').style.display = ''; + document.getElementById('loginForm').hidden = true; + document.getElementById('changePwSection').hidden = false; + document.getElementById('login-panel-title').textContent = 'New password'; + document.getElementById('loginPanelSubtitle').textContent = pendingUsername; + document.getElementById('newPw').focus(); return; } localStorage.setItem('qdb_token', d.token); @@ -115,22 +148,26 @@ export async function loginPage() { navigate('#/'); } catch (err) { errEl.textContent = err.message; - errEl.style.display = ''; + errEl.hidden = false; + } finally { + setFormBusy('loginForm', false, 'Signing in…'); } }); document.getElementById('changePwForm').addEventListener('submit', async (e) => { e.preventDefault(); const errEl = document.getElementById('changePwError'); - errEl.style.display = 'none'; + errEl.hidden = true; const newPw = document.getElementById('newPw').value; const confirm = document.getElementById('newPwConfirm').value; if (newPw !== confirm) { errEl.textContent = 'Passwords do not match'; - errEl.style.display = ''; + errEl.hidden = false; return; } const oldPw = document.getElementById('password').value; + + setFormBusy('changePwForm', true, 'Saving…'); try { const headers = { 'Content-Type': 'application/json', @@ -147,13 +184,13 @@ export async function loginPage() { const json = await res.json(); if (!json.ok) { errEl.textContent = json.error?.message || 'Password change failed'; - errEl.style.display = ''; + errEl.hidden = false; return; } const d = json.data; if (d.role === 'coach') { errEl.textContent = 'Counselor accounts can only sign in through the mobile app.'; - errEl.style.display = ''; + errEl.hidden = false; return; } localStorage.setItem('qdb_token', d.token); @@ -164,7 +201,9 @@ export async function loginPage() { navigate('#/'); } catch (err) { errEl.textContent = err.message; - errEl.style.display = ''; + errEl.hidden = false; + } finally { + setFormBusy('changePwForm', false, 'Saving…'); } }); }