adityasync commited on
Commit
f7864d7
·
1 Parent(s): 01cb704

feat: Implement dark mode functionality and refine various frontend layout styles.

Browse files
Files changed (3) hide show
  1. frontend/app.js +44 -1
  2. frontend/index.html +122 -129
  3. frontend/styles.css +221 -9
frontend/app.js CHANGED
@@ -88,6 +88,9 @@ const physicianNotesInput = document.getElementById("physicianNotesInput");
88
  const confirmShareButton = document.getElementById("confirmShareButton");
89
  const shareEmailInput = document.getElementById("shareEmailInput");
90
  const shareMessageInput = document.getElementById("shareMessageInput");
 
 
 
91
 
92
  const analysisMessages = [
93
  "Processing 324 slices",
@@ -100,6 +103,7 @@ wireUpload();
100
  wireActions();
101
  wireModals();
102
  wireTechnicalTabs();
 
103
  renderRecentUploads();
104
  renderHistoryPage();
105
  updateNavState();
@@ -259,6 +263,45 @@ function wireModals() {
259
  });
260
  }
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  function wireTechnicalTabs() {
263
  technicalTabs.forEach((tab) => {
264
  tab.addEventListener("click", () => {
@@ -660,7 +703,7 @@ function renderHistoryPage() {
660
 
661
  if (!items.length) {
662
  historyGroups.innerHTML = `
663
- <article class="card" style="max-width: var(--content-width);">
664
  <h3>No scans yet</h3>
665
  <p class="summary-text">Analyze a CT scan to start building your history.</p>
666
  </article>
 
88
  const confirmShareButton = document.getElementById("confirmShareButton");
89
  const shareEmailInput = document.getElementById("shareEmailInput");
90
  const shareMessageInput = document.getElementById("shareMessageInput");
91
+ const themeRadios = Array.from(document.querySelectorAll("input[name='theme']"));
92
+ const THEME_KEY = "oncovision.theme";
93
+ const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
94
 
95
  const analysisMessages = [
96
  "Processing 324 slices",
 
103
  wireActions();
104
  wireModals();
105
  wireTechnicalTabs();
106
+ wireThemeSelector();
107
  renderRecentUploads();
108
  renderHistoryPage();
109
  updateNavState();
 
263
  });
264
  }
265
 
266
+ function wireThemeSelector() {
267
+ const stored = localStorage.getItem(THEME_KEY) || "auto";
268
+ themeRadios.forEach((input) => {
269
+ input.checked = input.value === stored;
270
+ input.addEventListener("change", () => {
271
+ localStorage.setItem(THEME_KEY, input.value);
272
+ applyTheme(input.value);
273
+ });
274
+ });
275
+
276
+ applyTheme(stored);
277
+ const handleSystemThemeChange = () => {
278
+ if (localStorage.getItem(THEME_KEY) === "auto") {
279
+ applyTheme("auto");
280
+ }
281
+ };
282
+
283
+ if (typeof prefersDarkScheme.addEventListener === "function") {
284
+ prefersDarkScheme.addEventListener("change", handleSystemThemeChange);
285
+ } else if (typeof prefersDarkScheme.addListener === "function") {
286
+ prefersDarkScheme.addListener(handleSystemThemeChange);
287
+ }
288
+ }
289
+
290
+ function applyTheme(preference) {
291
+ const resolved =
292
+ preference === "dark"
293
+ ? "dark"
294
+ : preference === "light"
295
+ ? "light"
296
+ : prefersDarkScheme.matches
297
+ ? "dark"
298
+ : "light";
299
+
300
+ document.body.classList.toggle("theme-dark", resolved === "dark");
301
+ document.body.classList.toggle("theme-light", resolved === "light");
302
+ document.documentElement.dataset.theme = preference;
303
+ }
304
+
305
  function wireTechnicalTabs() {
306
  technicalTabs.forEach((tab) => {
307
  tab.addEventListener("click", () => {
 
703
 
704
  if (!items.length) {
705
  historyGroups.innerHTML = `
706
+ <article class="card">
707
  <h3>No scans yet</h3>
708
  <p class="summary-text">Analyze a CT scan to start building your history.</p>
709
  </article>
frontend/index.html CHANGED
@@ -57,7 +57,7 @@
57
  <section class="hero">
58
  <div class="hero__copy page-entrance">
59
  <p class="eyebrow">Clinical-grade AI review</p>
60
- <h1>Detect lung cancer earlier with AI-powered analysis.</h1>
61
  <p class="hero__subtitle">Calm, focused software for CT scan upload, nodule detection, and risk review.</p>
62
  <p class="hero__lede">
63
  OncoVision-X transforms complex chest CT volumes into clear findings, actionable next steps,
@@ -639,142 +639,135 @@ print(response.json()["analysis"])</code></pre>
639
  </footer>
640
  </div>
641
 
642
- <div class="modal-backdrop" id="modalBackdrop" hidden>
643
- <div class="modal modal--settings" id="settingsModal" hidden>
644
- <div class="modal__header">
645
- <h2>Settings</h2>
646
- <button class="icon-close" data-close-modal type="button">×</button>
647
- </div>
648
- <div class="modal__body">
649
- <section class="settings-group">
650
- <h3>Account</h3>
651
- <p>Name: Dr. Sarah Chen</p>
652
- <p>Email: sarah@hospital.com</p>
653
- </section>
654
- <section class="settings-group">
655
- <h3>Preferences</h3>
656
- <label><input type="checkbox" checked> Email notifications</label>
657
- <label><input type="checkbox" checked> Auto-save scans</label>
658
- <label><input type="checkbox"> Share anonymous data</label>
659
- </section>
660
- <section class="settings-group">
661
- <h3>Display</h3>
662
- <label><input type="radio" name="theme" checked> Light</label>
663
- <label><input type="radio" name="theme"> Auto</label>
664
- <label><input type="radio" name="theme"> Dark</label>
665
- </section>
666
- </div>
667
- <div class="modal__footer">
668
- <button class="button button--primary" data-close-modal type="button">Save Changes</button>
669
  </div>
670
- </div>
671
 
672
- <div class="modal" id="helpModal" hidden>
673
- <div class="modal__header">
674
- <h2>Documentation</h2>
675
- <button class="icon-close" data-close-modal type="button">×</button>
676
- </div>
677
- <div class="modal__body">
678
- <input class="search-input" type="search" placeholder="Search workflow notes">
679
- <section class="settings-group">
680
- <h3>Getting Started</h3>
681
- <p>Upload one CT volume or use the sample study from the upload page.</p>
682
- <p>Wait for preprocessing, candidate review, and report assembly to complete before navigating away.</p>
683
- <p>Open the results page to inspect the scan views, summary banner, nodule cards, and timing metrics.</p>
684
- </section>
685
- <section class="settings-group">
686
- <h3>Reading Results</h3>
687
- <p>Risk labels summarize the highest malignancy probability found in the current scan.</p>
688
- <p>Each nodule card lists location, detection confidence, malignancy probability, and follow-up guidance.</p>
689
- <p>Use the CT visualization and the exported PDF together when preparing printouts or handoffs.</p>
690
- </section>
691
- <section class="settings-group">
692
- <h3>Exports and Sharing</h3>
693
- <p>Use Export PDF for a formatted report with dedicated imaging pages suitable for printing.</p>
694
- <p>Use Share to prepare a review link and message for another clinician or collaborator.</p>
695
- <p>Use Export Image when you only need the current visualization panel.</p>
696
- </section>
697
- <section class="settings-group">
698
- <h3>Troubleshooting</h3>
699
- <p>If a scan does not appear, confirm the file is `.nii`, `.nii.gz`, `.mhd`, `.npy`, or `.npz`.</p>
700
- <p>If the PDF layout looks outdated, refresh the browser before generating a new report.</p>
701
- <p>If results look unexpected, compare the current scan against prior exports from the History page.</p>
702
- </section>
703
- </div>
704
- <div class="modal__footer">
705
- <button class="button button--primary" data-close-modal type="button">Close</button>
 
706
  </div>
707
- </div>
708
 
709
- <div class="modal" id="exportModal" hidden>
710
- <div class="modal__header">
711
- <h2>Generate PDF Report</h2>
712
- <button class="icon-close" data-close-modal type="button">×</button>
713
- </div>
714
- <div class="modal__body">
715
- <section class="settings-group">
716
- <h3>Include in report</h3>
717
- <label><input type="checkbox" checked> Cover page</label>
718
- <label><input type="checkbox" checked> Executive summary</label>
719
- <label><input type="checkbox" checked> Detailed nodule findings</label>
720
- <label><input type="checkbox" checked> CT scan visualization</label>
721
- <label><input type="checkbox" checked> Technical metadata</label>
722
- <label><input type="checkbox" checked> Medical disclaimer</label>
723
- </section>
724
- <section class="settings-group">
725
- <h3>Report Style</h3>
726
- <label><input type="radio" name="report-style"> Standard</label>
727
- <label><input type="radio" name="report-style" checked> Professional</label>
728
- <label><input type="radio" name="report-style"> Compact</label>
729
- </section>
730
- <section class="settings-group">
731
- <h3>Patient Information</h3>
732
- <input class="search-input" id="patientNameInput" type="text" placeholder="Name">
733
- <input class="search-input" id="patientMrnInput" type="text" placeholder="MRN">
734
- <input class="search-input" id="patientDobInput" type="text" placeholder="DOB">
735
- </section>
736
- <section class="settings-group">
737
- <h3>Physician Notes</h3>
738
- <textarea class="notes-input" id="physicianNotesInput" placeholder="Add clinical context..."></textarea>
739
- </section>
740
- </div>
741
- <div class="modal__footer">
742
- <button class="button button--ghost" data-close-modal type="button">Cancel</button>
743
- <button class="button button--secondary" id="previewExportButton" type="button">Preview</button>
744
- <button class="button button--primary" id="generateExportButton" type="button">Generate PDF</button>
745
  </div>
746
- </div>
747
 
748
- <div class="modal" id="shareModal" hidden>
749
- <div class="modal__header">
750
- <h2>Share Scan Results</h2>
751
- <button class="icon-close" data-close-modal type="button">×</button>
752
- </div>
753
- <div class="modal__body">
754
- <section class="settings-group">
755
- <h3>Share Method</h3>
756
- <label><input type="radio" name="share-method"> Secure Link</label>
757
- <label><input type="radio" name="share-method" checked> Email</label>
758
- <label><input type="radio" name="share-method"> Download QR Code</label>
759
- </section>
760
- <section class="settings-group">
761
- <h3>Email</h3>
762
- <input class="search-input" id="shareEmailInput" type="email" placeholder="physician@example.com">
763
- <textarea class="notes-input" id="shareMessageInput" placeholder="Please review this scan..."></textarea>
764
- </section>
765
- <section class="settings-group">
766
- <h3>Security Options</h3>
767
- <label><input type="checkbox" checked> Require password</label>
768
- <label><input type="checkbox" checked> Notify me when viewed</label>
769
- <label><input type="checkbox"> Disable after first view</label>
770
- </section>
771
- </div>
772
- <div class="modal__footer">
773
- <button class="button button--ghost" data-close-modal type="button">Cancel</button>
774
- <button class="button button--primary" id="confirmShareButton" type="button">Share Results</button>
 
775
  </div>
776
  </div>
777
- </div>
778
 
779
  <template id="noduleCardTemplate">
780
  <article class="nodule-card">
 
57
  <section class="hero">
58
  <div class="hero__copy page-entrance">
59
  <p class="eyebrow">Clinical-grade AI review</p>
60
+ <h1>Detect lung cancer earlier with <span class="nowrap">AI-powered</span> analysis.</h1>
61
  <p class="hero__subtitle">Calm, focused software for CT scan upload, nodule detection, and risk review.</p>
62
  <p class="hero__lede">
63
  OncoVision-X transforms complex chest CT volumes into clear findings, actionable next steps,
 
639
  </footer>
640
  </div>
641
 
642
+ <div class="modal-backdrop" id="modalBackdrop" hidden>
643
+ <div class="modal modal--settings" id="settingsModal" hidden>
644
+ <div class="modal__header">
645
+ <h2>Settings</h2>
646
+ <button class="icon-close" data-close-modal type="button">×</button>
647
+ </div>
648
+ <div class="modal__body">
649
+ <section class="settings-group">
650
+ <h3>Preferences</h3>
651
+ <label><input type="checkbox" checked> Email notifications</label>
652
+ <label><input type="checkbox" checked> Auto-save scans</label>
653
+ <label><input type="checkbox"> Share anonymous data</label>
654
+ </section>
655
+ <section class="settings-group">
656
+ <h3>Display</h3>
657
+ <label><input type="radio" name="theme" value="light" checked> Light</label>
658
+ <label><input type="radio" name="theme" value="auto"> Auto</label>
659
+ <label><input type="radio" name="theme" value="dark"> Dark</label>
660
+ </section>
661
+ </div>
662
+ <div class="modal__footer">
663
+ <button class="button button--primary" data-close-modal type="button">Save Changes</button>
664
+ </div>
 
 
 
 
665
  </div>
 
666
 
667
+ <div class="modal" id="helpModal" hidden>
668
+ <div class="modal__header">
669
+ <h2>Documentation</h2>
670
+ <button class="icon-close" data-close-modal type="button">×</button>
671
+ </div>
672
+ <div class="modal__body">
673
+ <input class="search-input" type="search" placeholder="Search workflow notes">
674
+ <section class="settings-group">
675
+ <h3>Getting Started</h3>
676
+ <p>Upload one CT volume or use the sample study from the upload page.</p>
677
+ <p>Wait for preprocessing, candidate review, and report assembly to complete before navigating away.</p>
678
+ <p>Open the results page to inspect the scan views, summary banner, nodule cards, and timing metrics.</p>
679
+ </section>
680
+ <section class="settings-group">
681
+ <h3>Reading Results</h3>
682
+ <p>Risk labels summarize the highest malignancy probability found in the current scan.</p>
683
+ <p>Each nodule card lists location, detection confidence, malignancy probability, and follow-up guidance.</p>
684
+ <p>Use the CT visualization and the exported PDF together when preparing printouts or handoffs.</p>
685
+ </section>
686
+ <section class="settings-group">
687
+ <h3>Exports and Sharing</h3>
688
+ <p>Use Export PDF for a formatted report with dedicated imaging pages suitable for printing.</p>
689
+ <p>Use Share to prepare a review link and message for another clinician or collaborator.</p>
690
+ <p>Use Export Image when you only need the current visualization panel.</p>
691
+ </section>
692
+ <section class="settings-group">
693
+ <h3>Troubleshooting</h3>
694
+ <p>If a scan does not appear, confirm the file is `.nii`, `.nii.gz`, `.mhd`, `.npy`, or `.npz`.</p>
695
+ <p>If the PDF layout looks outdated, refresh the browser before generating a new report.</p>
696
+ <p>If results look unexpected, compare the current scan against prior exports from the History page.</p>
697
+ </section>
698
+ </div>
699
+ <div class="modal__footer">
700
+ <button class="button button--primary" data-close-modal type="button">Close</button>
701
+ </div>
702
  </div>
 
703
 
704
+ <div class="modal" id="exportModal" hidden>
705
+ <div class="modal__header">
706
+ <h2>Generate PDF Report</h2>
707
+ <button class="icon-close" data-close-modal type="button">×</button>
708
+ </div>
709
+ <div class="modal__body">
710
+ <section class="settings-group">
711
+ <h3>Include in report</h3>
712
+ <label><input type="checkbox" checked> Cover page</label>
713
+ <label><input type="checkbox" checked> Executive summary</label>
714
+ <label><input type="checkbox" checked> Detailed nodule findings</label>
715
+ <label><input type="checkbox" checked> CT scan visualization</label>
716
+ <label><input type="checkbox" checked> Technical metadata</label>
717
+ <label><input type="checkbox" checked> Medical disclaimer</label>
718
+ </section>
719
+ <section class="settings-group">
720
+ <h3>Report Style</h3>
721
+ <p class="summary-text">Professional template only — curated for clinical handoff.</p>
722
+ </section>
723
+ <section class="settings-group">
724
+ <h3>Patient Information</h3>
725
+ <input class="search-input" id="patientNameInput" type="text" placeholder="Name">
726
+ <input class="search-input" id="patientMrnInput" type="text" placeholder="MRN">
727
+ <input class="search-input" id="patientDobInput" type="text" placeholder="DOB">
728
+ </section>
729
+ <section class="settings-group">
730
+ <h3>Physician Notes</h3>
731
+ <textarea class="notes-input" id="physicianNotesInput" placeholder="Add clinical context..."></textarea>
732
+ </section>
733
+ </div>
734
+ <div class="modal__footer">
735
+ <button class="button button--ghost" data-close-modal type="button">Cancel</button>
736
+ <button class="button button--secondary" id="previewExportButton" type="button">Preview</button>
737
+ <button class="button button--primary" id="generateExportButton" type="button">Generate PDF</button>
738
+ </div>
 
739
  </div>
 
740
 
741
+ <div class="modal" id="shareModal" hidden>
742
+ <div class="modal__header">
743
+ <h2>Share Scan Results</h2>
744
+ <button class="icon-close" data-close-modal type="button">×</button>
745
+ </div>
746
+ <div class="modal__body">
747
+ <section class="settings-group">
748
+ <h3>Share Method</h3>
749
+ <label><input type="radio" name="share-method"> Secure Link</label>
750
+ <label><input type="radio" name="share-method" checked> Email</label>
751
+ <label><input type="radio" name="share-method"> Download QR Code</label>
752
+ </section>
753
+ <section class="settings-group">
754
+ <h3>Email</h3>
755
+ <input class="search-input" id="shareEmailInput" type="email" placeholder="physician@example.com">
756
+ <textarea class="notes-input" id="shareMessageInput" placeholder="Please review this scan..."></textarea>
757
+ </section>
758
+ <section class="settings-group">
759
+ <h3>Security Options</h3>
760
+ <label><input type="checkbox" checked> Require password</label>
761
+ <label><input type="checkbox" checked> Notify me when viewed</label>
762
+ <label><input type="checkbox"> Disable after first view</label>
763
+ </section>
764
+ </div>
765
+ <div class="modal__footer">
766
+ <button class="button button--ghost" data-close-modal type="button">Cancel</button>
767
+ <button class="button button--primary" id="confirmShareButton" type="button">Share Results</button>
768
+ </div>
769
  </div>
770
  </div>
 
771
 
772
  <template id="noduleCardTemplate">
773
  <article class="nodule-card">
frontend/styles.css CHANGED
@@ -93,6 +93,10 @@ a {
93
  text-decoration: none;
94
  }
95
 
 
 
 
 
96
  button {
97
  border: 0;
98
  background: none;
@@ -592,7 +596,7 @@ a:focus-visible,
592
  }
593
 
594
  .page-header {
595
- max-width: var(--content-width);
596
  padding: 56px 0 20px;
597
  }
598
 
@@ -611,9 +615,11 @@ a:focus-visible,
611
 
612
  .upload-layout {
613
  display: grid;
614
- grid-template-columns: minmax(0, 720px) minmax(280px, 360px);
615
  gap: 32px;
 
616
  align-items: start;
 
 
617
  }
618
 
619
  .upload-focus {
@@ -1250,7 +1256,7 @@ a:focus-visible,
1250
  }
1251
 
1252
  .history-toolbar {
1253
- max-width: var(--content-width);
1254
  display: flex;
1255
  flex-wrap: wrap;
1256
  gap: 12px;
@@ -1261,7 +1267,7 @@ a:focus-visible,
1261
  }
1262
 
1263
  .history-group {
1264
- max-width: var(--content-width);
1265
  padding-top: 28px;
1266
  }
1267
 
@@ -1296,9 +1302,9 @@ a:focus-visible,
1296
  }
1297
 
1298
  .technical-tabs {
1299
- max-width: 1120px;
1300
- display: flex;
1301
- flex-wrap: wrap;
1302
  gap: 12px;
1303
  }
1304
 
@@ -1310,7 +1316,7 @@ a:focus-visible,
1310
  background: rgba(255, 255, 255, 0.9);
1311
  color: var(--text-secondary);
1312
  box-shadow: var(--shadow-sm);
1313
- white-space: nowrap;
1314
  }
1315
 
1316
  .technical-tab.is-active {
@@ -1320,7 +1326,7 @@ a:focus-visible,
1320
  }
1321
 
1322
  .technical-content {
1323
- max-width: 1120px;
1324
  }
1325
 
1326
  .technical-card {
@@ -2004,3 +2010,209 @@ a:focus-visible,
2004
  line-height: 1.5;
2005
  }
2006
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  text-decoration: none;
94
  }
95
 
96
+ .nowrap {
97
+ white-space: nowrap;
98
+ }
99
+
100
  button {
101
  border: 0;
102
  background: none;
 
596
  }
597
 
598
  .page-header {
599
+ width: 100%;
600
  padding: 56px 0 20px;
601
  }
602
 
 
615
 
616
  .upload-layout {
617
  display: grid;
 
618
  gap: 32px;
619
+ grid-template-columns: minmax(0, 1.2fr) minmax(280px, 360px);
620
  align-items: start;
621
+ width: min(100%, 1120px);
622
+ margin: 0 auto;
623
  }
624
 
625
  .upload-focus {
 
1256
  }
1257
 
1258
  .history-toolbar {
1259
+ width: 100%;
1260
  display: flex;
1261
  flex-wrap: wrap;
1262
  gap: 12px;
 
1267
  }
1268
 
1269
  .history-group {
1270
+ width: 100%;
1271
  padding-top: 28px;
1272
  }
1273
 
 
1302
  }
1303
 
1304
  .technical-tabs {
1305
+ width: 100%;
1306
+ display: grid;
1307
+ grid-template-columns: repeat(4, minmax(0, 1fr));
1308
  gap: 12px;
1309
  }
1310
 
 
1316
  background: rgba(255, 255, 255, 0.9);
1317
  color: var(--text-secondary);
1318
  box-shadow: var(--shadow-sm);
1319
+ white-space: normal;
1320
  }
1321
 
1322
  .technical-tab.is-active {
 
1326
  }
1327
 
1328
  .technical-content {
1329
+ width: 100%;
1330
  }
1331
 
1332
  .technical-card {
 
2010
  line-height: 1.5;
2011
  }
2012
  }
2013
+
2014
+ body.theme-dark {
2015
+ background: #010101;
2016
+ color: #f4f4f5;
2017
+ }
2018
+
2019
+ body.theme-dark .topbar__inner {
2020
+ background: rgba(0, 0, 0, 0.9);
2021
+ border-color: rgba(255, 255, 255, 0.08);
2022
+ box-shadow: 0 30px 60px rgba(0, 0, 0, 0.85);
2023
+ }
2024
+
2025
+ body.theme-dark .nav a {
2026
+ color: rgba(255, 255, 255, 0.8);
2027
+ }
2028
+
2029
+ body.theme-dark .nav a.is-active,
2030
+ body.theme-dark .nav a:hover {
2031
+ color: #fff;
2032
+ }
2033
+
2034
+ body.theme-dark h1,
2035
+ body.theme-dark h2,
2036
+ body.theme-dark h3,
2037
+ body.theme-dark h4,
2038
+ body.theme-dark h5,
2039
+ body.theme-dark h6,
2040
+ body.theme-dark p,
2041
+ body.theme-dark span,
2042
+ body.theme-dark strong,
2043
+ body.theme-dark small {
2044
+ color: rgba(255, 255, 255, 0.95);
2045
+ }
2046
+
2047
+ body.theme-dark .page-header h1,
2048
+ body.theme-dark .page-header h2,
2049
+ body.theme-dark .history-toolbar span,
2050
+ body.theme-dark .hero__subtitle,
2051
+ body.theme-dark .lead-text,
2052
+ body.theme-dark .cta-panel h2,
2053
+ body.theme-dark .page-header__actions button {
2054
+ color: #fff;
2055
+ }
2056
+
2057
+ body.theme-dark .hero h1,
2058
+ body.theme-dark .hero__subtitle,
2059
+ body.theme-dark .hero__lede,
2060
+ body.theme-dark .section-copy h2,
2061
+ body.theme-dark .section-copy p,
2062
+ body.theme-dark .panel-header h2,
2063
+ body.theme-dark .panel-header p,
2064
+ body.theme-dark .summary-text,
2065
+ body.theme-dark .feature-card h3,
2066
+ body.theme-dark .stat-card strong,
2067
+ body.theme-dark .history-card h3,
2068
+ body.theme-dark .history-card__file,
2069
+ body.theme-dark .history-card__date,
2070
+ body.theme-dark .history-card__score,
2071
+ body.theme-dark .risk-banner__label,
2072
+ body.theme-dark .legend span,
2073
+ body.theme-dark .results-meta,
2074
+ body.theme-dark .analysis-detail,
2075
+ body.theme-dark .breadcrumb {
2076
+ color: rgba(255, 255, 255, 0.8);
2077
+ }
2078
+
2079
+ body.theme-dark .card,
2080
+ body.theme-dark .modal,
2081
+ body.theme-dark .upload-zone,
2082
+ body.theme-dark .hero__visual-card,
2083
+ body.theme-dark .results-main,
2084
+ body.theme-dark .results-sidebar .card,
2085
+ body.theme-dark .history-card,
2086
+ body.theme-dark .stat-card,
2087
+ body.theme-dark .feature-card,
2088
+ body.theme-dark .step-card,
2089
+ body.theme-dark .cta-panel,
2090
+ body.theme-dark .sample-card,
2091
+ body.theme-dark .panel-header,
2092
+ body.theme-dark .technical-card,
2093
+ body.theme-dark .media-card,
2094
+ body.theme-dark .code-card,
2095
+ body.theme-dark .floating-bar,
2096
+ body.theme-dark .upload-focus,
2097
+ body.theme-dark .technical-columns,
2098
+ body.theme-dark .metric-table div,
2099
+ body.theme-dark .selected-file-panel,
2100
+ body.theme-dark .file-facts div,
2101
+ body.theme-dark .timing-grid div,
2102
+ body.theme-dark .scan-detail-grid div,
2103
+ body.theme-dark .nodule-card,
2104
+ body.theme-dark .recent-upload-item,
2105
+ body.theme-dark .empty-state {
2106
+ background: rgba(0, 0, 0, 0.85);
2107
+ border-color: rgba(255, 255, 255, 0.08);
2108
+ box-shadow: 0 25px 45px rgba(0, 0, 0, 0.9);
2109
+ }
2110
+
2111
+ body.theme-dark .results-main,
2112
+ body.theme-dark .results-sidebar .card {
2113
+ border: 1px solid rgba(255, 255, 255, 0.06);
2114
+ }
2115
+
2116
+ body.theme-dark .modal-backdrop {
2117
+ background: rgba(0, 0, 0, 0.78);
2118
+ }
2119
+
2120
+ body.theme-dark .upload-zone__icon,
2121
+ body.theme-dark .status-banner {
2122
+ background: rgba(255, 255, 255, 0.03);
2123
+ border: 1px solid rgba(255, 255, 255, 0.08);
2124
+ color: #fff;
2125
+ }
2126
+
2127
+ body.theme-dark .risk-banner {
2128
+ border-left-width: 1px;
2129
+ }
2130
+
2131
+ body.theme-dark .risk-banner--low {
2132
+ background: rgba(16, 185, 129, 0.12);
2133
+ border-left-color: var(--success);
2134
+ }
2135
+
2136
+ body.theme-dark .risk-banner--medium {
2137
+ background: rgba(245, 158, 11, 0.12);
2138
+ border-left-color: var(--warning);
2139
+ }
2140
+
2141
+ body.theme-dark .risk-banner--high {
2142
+ background: rgba(239, 68, 68, 0.12);
2143
+ border-left-color: var(--alert);
2144
+ }
2145
+
2146
+ body.theme-dark .analysis-view h1 {
2147
+ color: #fff;
2148
+ }
2149
+
2150
+ body.theme-dark .analysis-steps span {
2151
+ background: rgba(255, 255, 255, 0.08);
2152
+ color: #f4f4f5;
2153
+ border: 1px solid rgba(255, 255, 255, 0.12);
2154
+ }
2155
+
2156
+ body.theme-dark input,
2157
+ body.theme-dark textarea,
2158
+ body.theme-dark select,
2159
+ body.theme-dark .search-input,
2160
+ body.theme-dark .filter-select {
2161
+ background: rgba(0, 0, 0, 0.92);
2162
+ border-color: rgba(255, 255, 255, 0.2);
2163
+ color: #f4f4f5;
2164
+ }
2165
+
2166
+ body.theme-dark ::placeholder {
2167
+ color: rgba(255, 255, 255, 0.45);
2168
+ }
2169
+
2170
+ body.theme-dark .scroll-hint span {
2171
+ background: rgba(255, 255, 255, 0.5);
2172
+ }
2173
+
2174
+ body.theme-dark .upload-divider span {
2175
+ color: rgba(255, 255, 255, 0.45);
2176
+ }
2177
+
2178
+ body.theme-dark .icon-button {
2179
+ background: rgba(255, 255, 255, 0.05);
2180
+ border-color: rgba(255, 255, 255, 0.12);
2181
+ color: #fff;
2182
+ }
2183
+
2184
+ body.theme-dark .icon-button:hover {
2185
+ background: rgba(255, 255, 255, 0.12);
2186
+ border-color: rgba(255, 255, 255, 0.2);
2187
+ }
2188
+
2189
+ body.theme-dark .footer,
2190
+ body.theme-dark .footer-grid,
2191
+ body.theme-dark .footer-strip {
2192
+ color: rgba(255, 255, 255, 0.7);
2193
+ }
2194
+
2195
+ body.theme-dark .footer-link,
2196
+ body.theme-dark .footer-heading {
2197
+ color: #fff;
2198
+ }
2199
+
2200
+ body.theme-dark .technical-tabs {
2201
+ background: rgba(0, 0, 0, 0.95);
2202
+ border: 1px solid rgba(255, 255, 255, 0.08);
2203
+ padding: 14px;
2204
+ border-radius: 16px;
2205
+ }
2206
+
2207
+ body.theme-dark .technical-tab {
2208
+ background: rgba(255, 255, 255, 0.05);
2209
+ border-color: rgba(255, 255, 255, 0.12);
2210
+ color: #fff;
2211
+ box-shadow: none;
2212
+ }
2213
+
2214
+ body.theme-dark .technical-tab.is-active {
2215
+ background: rgba(255, 255, 255, 0.18);
2216
+ border-color: rgba(255, 255, 255, 0.2);
2217
+ color: #fff;
2218
+ }