diff --git a/generate_report.py b/generate_report.py index 978e79f..906d565 100644 --- a/generate_report.py +++ b/generate_report.py @@ -230,9 +230,23 @@ def main(): print(f"Error: Assets directory not found: {args.assets}") return 1 - # Validate template only if not using modern style - if args.style and args.style in MODERN_STYLES: + # Determine style from --style or --template + style = args.style + + # If no --style provided, check if --template points to a modern CSS file + if not style and args.template.endswith('.css'): + template_path = Path(args.template) + style_name = template_path.stem # e.g., "brutalism" from "brutalism.css" + if style_name in MODERN_STYLES: + style = style_name + + # Validate template/style + if style and style in MODERN_STYLES: template_path = SCRIPT_DIR / "templates" / "modern.html" + css_path = SCRIPT_DIR / "templates" / f"{style}.css" + if not css_path.exists(): + print(f"Error: CSS file not found: {css_path}") + return 1 else: template_path = SCRIPT_DIR / args.template @@ -240,7 +254,7 @@ def main(): print(f"Error: Template file not found: {template_path}") return 1 - generate_report(args.db, args.output, args.top, args.assets, args.template, args.background, args.style) + generate_report(args.db, args.output, args.top, args.assets, args.template, args.background, style) return 0 diff --git a/styles.py b/styles.py index a1b5dfd..3002cec 100644 --- a/styles.py +++ b/styles.py @@ -1,6 +1,10 @@ """CSS styles and theme configurations for modern templates.""" import json +from pathlib import Path + +# Directory where templates are located +TEMPLATES_DIR = Path(__file__).parent / "templates" # Theme configurations for Chart.js THEME_CONFIGS = { @@ -72,1829 +76,15 @@ THEME_CONFIGS = { } } -# CSS styles for each theme -THEME_CSS = { - "brutalism": """ - :root { - --bg-base: #ffffff; - --bg-primary: #ffffff; - --bg-secondary: #f0f0f0; - --bg-tertiary: #e0e0e0; - --text-primary: #000000; - --text-secondary: #333333; - --text-muted: #666666; - --border-color: #000000; - --border-width: 3px; - --accent-color: #ff0000; - --accent-secondary: #0000ff; - --accent-tertiary: #ffff00; - --selection-bg: #ffff00; - --selection-text: #000000; - --shadow-offset: 6px; - } - - [data-theme="dark"] { - --bg-base: #000000; - --bg-primary: #000000; - --bg-secondary: #111111; - --bg-tertiary: #222222; - --text-primary: #ffffff; - --text-secondary: #cccccc; - --text-muted: #888888; - --border-color: #ffffff; - --accent-color: #ff3333; - --accent-secondary: #3333ff; - --accent-tertiary: #ffff33; - --selection-bg: #ff3333; - --selection-text: #000000; - } - - @media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) { - --bg-base: #000000; - --bg-primary: #000000; - --bg-secondary: #111111; - --bg-tertiary: #222222; - --text-primary: #ffffff; - --text-secondary: #cccccc; - --text-muted: #888888; - --border-color: #ffffff; - --accent-color: #ff3333; - --accent-secondary: #3333ff; - --accent-tertiary: #ffff33; - --selection-bg: #ff3333; - --selection-text: #000000; - } - } - - * { - box-sizing: border-box; - margin: 0; - padding: 0; - } - - body { - font-family: 'Courier New', Courier, monospace; - font-size: 14px; - max-width: 1200px; - margin: 0 auto; - padding: 20px; - background-color: var(--bg-base); - background-image: __BACKGROUND_IMAGE_CUSTOM__; - background-size: cover; - background-position: center; - background-attachment: fixed; - min-height: 100vh; - color: var(--text-primary); - line-height: 1.4; - } - - /* Theme Toggle Button */ - .theme-toggle { - position: fixed; - top: 20px; - right: 20px; - z-index: 1000; - width: 50px; - height: 50px; - border: var(--border-width) solid var(--border-color); - background: var(--bg-primary); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border-color); - transition: transform 0.1s, box-shadow 0.1s; - } - - .theme-toggle:hover { - transform: translate(-2px, -2px); - box-shadow: calc(var(--shadow-offset) + 2px) calc(var(--shadow-offset) + 2px) 0 var(--border-color); - } - - .theme-toggle:active { - transform: translate(2px, 2px); - box-shadow: calc(var(--shadow-offset) - 2px) calc(var(--shadow-offset) - 2px) 0 var(--border-color); - } - - .theme-toggle .icon { - line-height: 1; - } - - .theme-toggle .icon-auto { - position: relative; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - } - - .theme-toggle .icon-auto::before, - .theme-toggle .icon-auto::after { - position: absolute; - font-size: 20px; - line-height: 1; - } - - .theme-toggle .icon-auto::before { - content: '\\2600\\FE0F'; - clip-path: inset(0 50% 0 0); - } - - .theme-toggle .icon-auto::after { - content: '\\D83C\\DF19'; - clip-path: inset(0 0 0 50%); - } - - /* Card Style */ - .card { - background: var(--bg-primary); - border: var(--border-width) solid var(--border-color); - box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border-color); - margin-bottom: 24px; - overflow: hidden; - } - - .card-header { - padding: 12px 16px; - border-bottom: var(--border-width) solid var(--border-color); - background: var(--accent-tertiary); - } - - .card-title { - font-size: 18px; - font-weight: 900; - text-transform: uppercase; - letter-spacing: 2px; - color: var(--text-primary); - } - - [data-theme="dark"] .card-title { - color: #000000; - } - - @media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) .card-title { - color: #000000; - } - } - - .card-content { - padding: 20px; - } - - .summaries-content { - padding: 0; - padding-top: 16px; - } - - /* Filters */ - .filters { - display: flex; - justify-content: center; - gap: 12px; - flex-wrap: wrap; - } - - .filter-label { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; - user-select: none; - padding: 8px 12px; - background: var(--bg-secondary); - border: 2px solid var(--border-color); - font-weight: 700; - text-transform: uppercase; - font-size: 12px; - transition: transform 0.1s, box-shadow 0.1s; - } - - .filter-label:hover { - transform: translate(-2px, -2px); - box-shadow: 4px 4px 0 var(--border-color); - } - - .filter-label input[type="checkbox"] { - appearance: none; - -webkit-appearance: none; - width: 20px; - height: 20px; - border: 2px solid var(--border-color); - background: var(--bg-primary); - cursor: pointer; - position: relative; - } - - .filter-label input[type="checkbox"]:checked { - background: var(--accent-color); - } - - .filter-label input[type="checkbox"]:checked::after { - content: '\\2715'; - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - font-size: 14px; - font-weight: 900; - color: var(--bg-primary); - } - - .filter-label .service-name { - text-transform: uppercase; - font-weight: 700; - color: var(--text-primary); - } - - .filter-label .service-count { - color: var(--text-muted); - font-size: 11px; - } - - /* Stats */ - .stats { - display: flex; - justify-content: center; - gap: 20px; - flex-wrap: wrap; - } - - .stat { - text-align: center; - padding: 20px 30px; - background: var(--bg-secondary); - border: var(--border-width) solid var(--border-color); - min-width: 150px; - } - - .stat-value { - font-size: 36px; - font-weight: 900; - color: var(--accent-color); - font-variant-numeric: tabular-nums; - } - - .stat-label { - font-size: 11px; - color: var(--text-muted); - margin-top: 4px; - text-transform: uppercase; - letter-spacing: 1px; - font-weight: 700; - } - - /* Charts */ - .charts-wrapper { - display: flex; - gap: 20px; - justify-content: center; - flex-wrap: wrap; - } - - .chart-container { - flex: 1; - min-width: 280px; - max-width: 450px; - padding: 16px; - background: var(--bg-secondary); - border: var(--border-width) solid var(--border-color); - } - - .chart-title { - font-size: 14px; - font-weight: 900; - text-align: center; - margin-bottom: 12px; - color: var(--text-primary); - text-transform: uppercase; - letter-spacing: 1px; - } - - @media (max-width: 700px) { - .charts-wrapper { - flex-direction: column; - align-items: center; - } - .chart-container { - max-width: 100%; - width: 100%; - } - } - - /* Tabs */ - .tabs { - display: flex; - gap: 0; - padding: 0 16px; - margin-bottom: -3px; - position: relative; - z-index: 1; - } - - .tab { - padding: 12px 24px; - cursor: pointer; - color: var(--text-secondary); - font-weight: 900; - text-transform: uppercase; - font-size: 12px; - letter-spacing: 1px; - background: var(--bg-tertiary); - border: var(--border-width) solid var(--border-color); - border-bottom: none; - margin-right: -3px; - transition: none; - } - - .tab:hover { - color: var(--text-primary); - background: var(--bg-secondary); - } - - .tab.active { - color: var(--accent-color); - background: var(--bg-secondary); - position: relative; - } - - .tab.active::after { - content: ''; - position: absolute; - bottom: -3px; - left: 0; - right: 0; - height: 3px; - background: var(--bg-secondary); - } - - .tab-content { - background: var(--bg-secondary); - border: var(--border-width) solid var(--border-color); - padding: 16px; - } - - .tab-panel { - display: none; - } - - .tab-panel.active { - display: block; - } - - /* Tables */ - .table-wrapper { - overflow-x: auto; - } - - table { - width: 100%; - border-collapse: collapse; - font-size: 13px; - } - - th { - padding: 12px 16px; - text-align: left; - font-weight: 900; - color: var(--text-primary); - border-bottom: var(--border-width) solid var(--border-color); - text-transform: uppercase; - font-size: 11px; - letter-spacing: 1px; - background: var(--bg-tertiary); - } - - td { - padding: 12px 16px; - border-bottom: 1px solid var(--border-color); - color: var(--text-primary); - } - - tr:hover td { - background: var(--selection-bg); - color: var(--selection-text); - } - - tr:hover .service-badge { - background: var(--border-color); - color: var(--bg-primary); - } - - tr:last-child td { - border-bottom: none; - } - - .time { - font-variant-numeric: tabular-nums; - font-weight: 700; - } - - .percent { - font-variant-numeric: tabular-nums; - text-align: right; - color: var(--text-muted); - font-weight: 700; - } - - .color-box { - display: inline-block; - width: 14px; - height: 14px; - margin-right: 8px; - vertical-align: middle; - border: 2px solid var(--border-color); - } - - .service-badge { - display: inline-block; - font-size: 10px; - padding: 2px 6px; - background: var(--bg-tertiary); - border: 1px solid var(--border-color); - color: var(--text-muted); - margin-left: 8px; - text-transform: uppercase; - font-weight: 700; - letter-spacing: 0.5px; - } - - .category-badge { - display: inline-block; - font-size: 10px; - padding: 2px 6px; - background: var(--accent-secondary); - border: 1px solid var(--border-color); - color: var(--bg-primary); - margin-left: 4px; - text-transform: uppercase; - font-weight: 700; - letter-spacing: 0.5px; - } - - .no-data { - text-align: center; - padding: 40px; - color: var(--text-muted); - font-weight: 700; - text-transform: uppercase; - } - - /* Others row expansion */ - .others-row { - cursor: pointer; - } - - .others-row td:first-child::before { - content: '\\25B6'; - display: inline-block; - margin-right: 8px; - font-size: 10px; - transition: transform 0.1s; - } - - .others-row.expanded td:first-child::before { - transform: rotate(90deg); - } - - .others-detail { - display: none; - background: var(--bg-tertiary); - } - - .others-detail.visible { - display: table-row; - } - - .others-detail td { - padding-left: 32px; - } - - /* Scrollbar */ - ::-webkit-scrollbar { - width: 12px; - height: 12px; - } - - ::-webkit-scrollbar-track { - background: var(--bg-tertiary); - border: 1px solid var(--border-color); - } - - ::-webkit-scrollbar-thumb { - background: var(--text-muted); - border: 1px solid var(--border-color); - } - - ::-webkit-scrollbar-thumb:hover { - background: var(--text-secondary); - } - - /* Responsive */ - @media (max-width: 600px) { - body { - padding: 12px; - } - - .stats { - gap: 12px; - } - - .stat { - padding: 16px 20px; - min-width: 100px; - } - - .stat-value { - font-size: 28px; - } - - .filter-label { - padding: 6px 10px; - font-size: 11px; - } - - .tabs { - padding: 0 12px; - } - - .tab { - padding: 10px 16px; - font-size: 11px; - } - - .theme-toggle { - width: 44px; - height: 44px; - font-size: 18px; - } - } - - /* Scroll to Top Button */ - .scroll-top { - position: fixed; - bottom: 20px; - right: 20px; - z-index: 1000; - width: 50px; - height: 50px; - border: var(--border-width) solid var(--border-color); - background: var(--bg-primary); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border-color); - opacity: 0; - visibility: hidden; - transition: opacity 0.2s, visibility 0.2s, transform 0.1s, box-shadow 0.1s; - } - - .scroll-top.visible { - opacity: 1; - visibility: visible; - } - - .scroll-top:hover { - transform: translate(-2px, -2px); - box-shadow: calc(var(--shadow-offset) + 2px) calc(var(--shadow-offset) + 2px) 0 var(--border-color); - } - - .scroll-top:active { - transform: translate(2px, 2px); - box-shadow: calc(var(--shadow-offset) - 2px) calc(var(--shadow-offset) - 2px) 0 var(--border-color); - } - - .scroll-top-arrow { - width: 0; - height: 0; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - border-bottom: 12px solid var(--text-primary); - } - - @media (max-width: 600px) { - .scroll-top { - width: 44px; - height: 44px; - } - - .scroll-top-arrow { - border-left-width: 8px; - border-right-width: 8px; - border-bottom-width: 10px; - } - } - """, - "glassmorphism": """ - :root { - --bg-primary: rgba(255, 255, 255, 0.25); - --bg-secondary: rgba(255, 255, 255, 0.15); - --bg-tertiary: rgba(255, 255, 255, 0.1); - --text-primary: #1a1a2e; - --text-secondary: #4a4a6a; - --text-muted: #6a6a8a; - --border-color: rgba(255, 255, 255, 0.3); - --shadow-color: rgba(0, 0, 0, 0.1); - --accent-color: #6366f1; - --accent-hover: #4f46e5; - --selection-bg: rgba(99, 102, 241, 0.3); - --glass-blur: 20px; - --card-radius: 16px; - } - - [data-theme="dark"] { - --bg-primary: rgba(30, 30, 50, 0.6); - --bg-secondary: rgba(40, 40, 70, 0.5); - --bg-tertiary: rgba(50, 50, 80, 0.4); - --text-primary: #f0f0f5; - --text-secondary: #c0c0d0; - --text-muted: #9090a0; - --border-color: rgba(255, 255, 255, 0.1); - --shadow-color: rgba(0, 0, 0, 0.3); - --accent-color: #818cf8; - --accent-hover: #6366f1; - --selection-bg: rgba(129, 140, 248, 0.3); - } - - @media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) { - --bg-primary: rgba(30, 30, 50, 0.6); - --bg-secondary: rgba(40, 40, 70, 0.5); - --bg-tertiary: rgba(50, 50, 80, 0.4); - --text-primary: #f0f0f5; - --text-secondary: #c0c0d0; - --text-muted: #9090a0; - --border-color: rgba(255, 255, 255, 0.1); - --shadow-color: rgba(0, 0, 0, 0.3); - --accent-color: #818cf8; - --accent-hover: #6366f1; - --selection-bg: rgba(129, 140, 248, 0.3); - } - } - - * { - box-sizing: border-box; - margin: 0; - padding: 0; - } - - body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; - font-size: 14px; - max-width: 1200px; - margin: 0 auto; - padding: 20px; - background-color: #667eea; - background-image: url('__BACKGROUND_IMAGE__'); - background-size: cover; - background-position: center; - background-attachment: fixed; - min-height: 100vh; - color: var(--text-primary); - line-height: 1.5; - } - - /* Theme Toggle Button */ - .theme-toggle { - position: fixed; - top: 20px; - right: 20px; - z-index: 1000; - width: 48px; - height: 48px; - border-radius: 50%; - border: 1px solid var(--border-color); - background: var(--bg-primary); - backdrop-filter: blur(var(--glass-blur)); - -webkit-backdrop-filter: blur(var(--glass-blur)); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - box-shadow: 0 8px 32px var(--shadow-color); - transition: all 0.3s ease; - } - - .theme-toggle:hover { - transform: scale(1.1); - background: var(--bg-secondary); - } - - .theme-toggle .icon { - transition: transform 0.3s ease; - } - - .theme-toggle .icon-auto { - position: relative; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - } - - .theme-toggle .icon-auto::before, - .theme-toggle .icon-auto::after { - position: absolute; - font-size: 20px; - line-height: 1; - } - - .theme-toggle .icon-auto::before { - content: '\\2600\\FE0F'; - clip-path: inset(0 50% 0 0); - } - - .theme-toggle .icon-auto::after { - content: '\\D83C\\DF19'; - clip-path: inset(0 0 0 50%); - } - - /* Card Style */ - .card { - background: var(--bg-primary); - backdrop-filter: blur(var(--glass-blur)); - -webkit-backdrop-filter: blur(var(--glass-blur)); - border-radius: var(--card-radius); - border: 1px solid var(--border-color); - box-shadow: 0 8px 32px var(--shadow-color); - margin-bottom: 24px; - overflow: hidden; - transition: all 0.3s ease; - } - - .card:hover { - box-shadow: 0 12px 40px var(--shadow-color); - } - - .card-header { - padding: 16px 20px; - border-bottom: 1px solid var(--border-color); - background: var(--bg-tertiary); - } - - .card-title { - font-size: 16px; - font-weight: 600; - color: var(--text-primary); - } - - .card-content { - padding: 20px; - } - - .summaries-content { - padding: 0; - padding-top: 16px; - } - - /* Filters */ - .filters { - display: flex; - justify-content: center; - gap: 16px; - flex-wrap: wrap; - } - - .filter-label { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; - user-select: none; - padding: 8px 16px; - border-radius: 24px; - background: var(--bg-secondary); - border: 1px solid var(--border-color); - transition: all 0.2s ease; - } - - .filter-label:hover { - background: var(--bg-tertiary); - } - - .filter-label input[type="checkbox"] { - appearance: none; - -webkit-appearance: none; - width: 18px; - height: 18px; - border-radius: 4px; - border: 2px solid var(--text-muted); - cursor: pointer; - position: relative; - transition: all 0.2s ease; - } - - .filter-label input[type="checkbox"]:checked { - background: var(--accent-color); - border-color: var(--accent-color); - } - - .filter-label input[type="checkbox"]:checked::after { - content: ''; - position: absolute; - left: 5px; - top: 2px; - width: 4px; - height: 8px; - border: solid white; - border-width: 0 2px 2px 0; - transform: rotate(45deg); - } - - .filter-label .service-name { - text-transform: capitalize; - font-weight: 500; - color: var(--text-primary); - } - - .filter-label .service-count { - color: var(--text-muted); - font-size: 12px; - } - - /* Stats */ - .stats { - display: flex; - justify-content: center; - gap: 24px; - flex-wrap: wrap; - } - - .stat { - text-align: center; - padding: 20px 32px; - background: var(--bg-secondary); - border-radius: 12px; - border: 1px solid var(--border-color); - min-width: 140px; - } - - .stat-value { - font-size: 28px; - font-weight: 700; - color: var(--accent-color); - font-variant-numeric: tabular-nums; - } - - .stat-label { - font-size: 12px; - color: var(--text-muted); - margin-top: 4px; - text-transform: uppercase; - letter-spacing: 0.5px; - } - - /* Charts */ - .charts-wrapper { - display: flex; - gap: 24px; - justify-content: center; - flex-wrap: wrap; - } - - .chart-container { - flex: 1; - min-width: 280px; - max-width: 450px; - padding: 16px; - background: var(--bg-secondary); - border-radius: 12px; - border: 1px solid var(--border-color); - } - - .chart-title { - font-size: 14px; - font-weight: 600; - text-align: center; - margin-bottom: 12px; - color: var(--text-primary); - } - - @media (max-width: 700px) { - .charts-wrapper { - flex-direction: column; - align-items: center; - } - .chart-container { - max-width: 100%; - width: 100%; - } - } - - /* Tabs */ - .tabs { - display: flex; - gap: 8px; - padding: 0 20px; - margin-bottom: -1px; - position: relative; - z-index: 1; - } - - .tab { - padding: 12px 24px; - cursor: pointer; - color: var(--text-secondary); - font-weight: 500; - border-radius: 12px 12px 0 0; - background: var(--bg-tertiary); - border: 1px solid transparent; - border-bottom: none; - transition: all 0.2s ease; - } - - .tab:hover { - color: var(--text-primary); - background: var(--bg-secondary); - } - - .tab.active { - color: var(--accent-color); - background: var(--bg-secondary); - border-color: var(--border-color); - } - - .tab-content { - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 0 12px 12px 12px; - padding: 16px; - } - - .tab-panel { - display: none; - } - - .tab-panel.active { - display: block; - } - - /* Tables */ - .table-wrapper { - overflow-x: auto; - } - - table { - width: 100%; - border-collapse: collapse; - font-size: 13px; - } - - th { - padding: 12px 16px; - text-align: left; - font-weight: 600; - color: var(--text-secondary); - border-bottom: 2px solid var(--border-color); - text-transform: uppercase; - font-size: 11px; - letter-spacing: 0.5px; - } - - td { - padding: 12px 16px; - border-bottom: 1px solid var(--border-color); - color: var(--text-primary); - } - - tr:hover td { - background: var(--selection-bg); - } - - tr:last-child td { - border-bottom: none; - } - - .time { - font-variant-numeric: tabular-nums; - font-weight: 500; - } - - .percent { - font-variant-numeric: tabular-nums; - text-align: right; - color: var(--text-muted); - } - - .color-box { - display: inline-block; - width: 12px; - height: 12px; - margin-right: 8px; - vertical-align: middle; - border-radius: 3px; - } - - .service-badge { - display: inline-block; - font-size: 10px; - padding: 2px 8px; - background: var(--bg-tertiary); - border-radius: 12px; - color: var(--text-muted); - margin-left: 8px; - text-transform: capitalize; - font-weight: 500; - } - - .category-badge { - display: inline-block; - font-size: 10px; - padding: 2px 8px; - background: rgba(99, 102, 241, 0.2); - border-radius: 12px; - color: var(--accent-color); - margin-left: 4px; - text-transform: capitalize; - font-weight: 500; - } - - .no-data { - text-align: center; - padding: 40px; - color: var(--text-muted); - } - - /* Others row expansion */ - .others-row { - cursor: pointer; - } - - .others-row td:first-child::before { - content: ''; - display: inline-block; - width: 0; - height: 0; - border-left: 6px solid var(--text-muted); - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - margin-right: 8px; - transition: transform 0.2s ease; - } - - .others-row.expanded td:first-child::before { - transform: rotate(90deg); - } - - .others-detail { - display: none; - background: var(--bg-tertiary); - } - - .others-detail.visible { - display: table-row; - } - - .others-detail td { - padding-left: 32px; - } - - /* Scrollbar */ - ::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - ::-webkit-scrollbar-track { - background: var(--bg-tertiary); - border-radius: 4px; - } - - ::-webkit-scrollbar-thumb { - background: var(--text-muted); - border-radius: 4px; - } - - ::-webkit-scrollbar-thumb:hover { - background: var(--text-secondary); - } - - /* Responsive */ - @media (max-width: 600px) { - body { - padding: 12px; - } - - .stats { - gap: 12px; - } - - .stat { - padding: 16px 20px; - min-width: 100px; - } - - .stat-value { - font-size: 22px; - } - - .filter-label { - padding: 6px 12px; - font-size: 13px; - } - - .tabs { - padding: 0 12px; - } - - .tab { - padding: 10px 16px; - font-size: 13px; - } - - .theme-toggle { - width: 40px; - height: 40px; - font-size: 16px; - } - } - - /* Scroll to Top Button */ - .scroll-top { - position: fixed; - bottom: 20px; - right: 20px; - z-index: 1000; - width: 48px; - height: 48px; - border-radius: 50%; - border: 1px solid var(--border-color); - background: var(--bg-primary); - backdrop-filter: blur(var(--glass-blur)); - -webkit-backdrop-filter: blur(var(--glass-blur)); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 8px 32px var(--shadow-color); - opacity: 0; - visibility: hidden; - transition: all 0.3s ease; - } - - .scroll-top.visible { - opacity: 1; - visibility: visible; - } - - .scroll-top:hover { - transform: scale(1.1); - background: var(--bg-secondary); - } - - .scroll-top-arrow { - width: 0; - height: 0; - border-left: 8px solid transparent; - border-right: 8px solid transparent; - border-bottom: 10px solid var(--text-primary); - } - - @media (max-width: 600px) { - .scroll-top { - width: 40px; - height: 40px; - } - - .scroll-top-arrow { - border-left-width: 6px; - border-right-width: 6px; - border-bottom-width: 8px; - } - } - """, - "neumorphism": """ - :root { - --bg-base: #e0e5ec; - --bg-primary: #e0e5ec; - --bg-secondary: #e0e5ec; - --bg-tertiary: #d1d9e6; - --text-primary: #2d3436; - --text-secondary: #4a5568; - --text-muted: #718096; - --accent-color: #6366f1; - --accent-hover: #4f46e5; - --selection-bg: rgba(99, 102, 241, 0.15); - --shadow-light: rgba(255, 255, 255, 0.8); - --shadow-dark: rgba(163, 177, 198, 0.6); - --shadow-inset-light: rgba(255, 255, 255, 0.7); - --shadow-inset-dark: rgba(163, 177, 198, 0.5); - --card-radius: 20px; - --border-color: transparent; - } - - [data-theme="dark"] { - --bg-base: #2d3436; - --bg-primary: #2d3436; - --bg-secondary: #2d3436; - --bg-tertiary: #353b3d; - --text-primary: #f0f0f5; - --text-secondary: #b0b8c0; - --text-muted: #8090a0; - --accent-color: #818cf8; - --accent-hover: #6366f1; - --selection-bg: rgba(129, 140, 248, 0.2); - --shadow-light: rgba(255, 255, 255, 0.05); - --shadow-dark: rgba(0, 0, 0, 0.4); - --shadow-inset-light: rgba(255, 255, 255, 0.03); - --shadow-inset-dark: rgba(0, 0, 0, 0.3); - } - - @media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) { - --bg-base: #2d3436; - --bg-primary: #2d3436; - --bg-secondary: #2d3436; - --bg-tertiary: #353b3d; - --text-primary: #f0f0f5; - --text-secondary: #b0b8c0; - --text-muted: #8090a0; - --accent-color: #818cf8; - --accent-hover: #6366f1; - --selection-bg: rgba(129, 140, 248, 0.2); - --shadow-light: rgba(255, 255, 255, 0.05); - --shadow-dark: rgba(0, 0, 0, 0.4); - --shadow-inset-light: rgba(255, 255, 255, 0.03); - --shadow-inset-dark: rgba(0, 0, 0, 0.3); - } - } - - * { - box-sizing: border-box; - margin: 0; - padding: 0; - } - - body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; - font-size: 14px; - max-width: 1200px; - margin: 0 auto; - padding: 20px; - background-color: var(--bg-base); - min-height: 100vh; - color: var(--text-primary); - line-height: 1.5; - } - - /* Theme Toggle Button */ - .theme-toggle { - position: fixed; - top: 20px; - right: 20px; - z-index: 1000; - width: 48px; - height: 48px; - border-radius: 50%; - border: none; - background: var(--bg-primary); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - box-shadow: - 6px 6px 12px var(--shadow-dark), - -6px -6px 12px var(--shadow-light); - transition: all 0.3s ease; - } - - .theme-toggle:hover { - transform: scale(1.05); - } - - .theme-toggle:active { - box-shadow: - inset 4px 4px 8px var(--shadow-inset-dark), - inset -4px -4px 8px var(--shadow-inset-light); - } - - .theme-toggle .icon { - transition: transform 0.3s ease; - } - - .theme-toggle .icon-auto { - position: relative; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - } - - .theme-toggle .icon-auto::before, - .theme-toggle .icon-auto::after { - position: absolute; - font-size: 20px; - line-height: 1; - } - - .theme-toggle .icon-auto::before { - content: '\\2600\\FE0F'; - clip-path: inset(0 50% 0 0); - } - - .theme-toggle .icon-auto::after { - content: '\\D83C\\DF19'; - clip-path: inset(0 0 0 50%); - } - - /* Card Style */ - .card { - background: var(--bg-primary); - border-radius: var(--card-radius); - box-shadow: - 8px 8px 16px var(--shadow-dark), - -8px -8px 16px var(--shadow-light); - margin-bottom: 24px; - overflow: hidden; - transition: all 0.3s ease; - } - - .card-header { - padding: 16px 20px; - background: var(--bg-tertiary); - border-radius: var(--card-radius) var(--card-radius) 0 0; - } - - .card-title { - font-size: 16px; - font-weight: 600; - color: var(--text-primary); - } - - .card-content { - padding: 20px; - } - - .summaries-content { - padding: 0; - padding-top: 16px; - } - - /* Filters */ - .filters { - display: flex; - justify-content: center; - gap: 16px; - flex-wrap: wrap; - } - - .filter-label { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; - user-select: none; - padding: 10px 18px; - border-radius: 30px; - background: var(--bg-secondary); - box-shadow: - 4px 4px 8px var(--shadow-dark), - -4px -4px 8px var(--shadow-light); - transition: all 0.2s ease; - } - - .filter-label:hover { - transform: translateY(-2px); - } - - .filter-label:active { - box-shadow: - inset 3px 3px 6px var(--shadow-inset-dark), - inset -3px -3px 6px var(--shadow-inset-light); - } - - .filter-label input[type="checkbox"] { - appearance: none; - -webkit-appearance: none; - width: 20px; - height: 20px; - border-radius: 6px; - background: var(--bg-secondary); - cursor: pointer; - position: relative; - transition: all 0.2s ease; - box-shadow: - inset 2px 2px 4px var(--shadow-inset-dark), - inset -2px -2px 4px var(--shadow-inset-light); - } - - .filter-label input[type="checkbox"]:checked { - background: var(--accent-color); - box-shadow: - inset 2px 2px 4px rgba(0, 0, 0, 0.2), - inset -2px -2px 4px rgba(255, 255, 255, 0.1); - } - - .filter-label input[type="checkbox"]:checked::after { - content: ''; - position: absolute; - left: 6px; - top: 3px; - width: 5px; - height: 9px; - border: solid white; - border-width: 0 2px 2px 0; - transform: rotate(45deg); - } - - .filter-label .service-name { - text-transform: capitalize; - font-weight: 500; - color: var(--text-primary); - } - - .filter-label .service-count { - color: var(--text-muted); - font-size: 12px; - } - - /* Stats */ - .stats { - display: flex; - justify-content: center; - gap: 24px; - flex-wrap: wrap; - } - - .stat { - text-align: center; - padding: 24px 36px; - background: var(--bg-secondary); - border-radius: 16px; - min-width: 140px; - box-shadow: - 6px 6px 12px var(--shadow-dark), - -6px -6px 12px var(--shadow-light); - } - - .stat-value { - font-size: 28px; - font-weight: 700; - color: var(--accent-color); - font-variant-numeric: tabular-nums; - } - - .stat-label { - font-size: 12px; - color: var(--text-muted); - margin-top: 4px; - text-transform: uppercase; - letter-spacing: 0.5px; - } - - /* Charts */ - .charts-wrapper { - display: flex; - gap: 24px; - justify-content: center; - flex-wrap: wrap; - } - - .chart-container { - flex: 1; - min-width: 280px; - max-width: 450px; - padding: 20px; - background: var(--bg-secondary); - border-radius: 16px; - box-shadow: - inset 4px 4px 8px var(--shadow-inset-dark), - inset -4px -4px 8px var(--shadow-inset-light); - } - - .chart-title { - font-size: 14px; - font-weight: 600; - text-align: center; - margin-bottom: 12px; - color: var(--text-primary); - } - - @media (max-width: 700px) { - .charts-wrapper { - flex-direction: column; - align-items: center; - } - .chart-container { - max-width: 100%; - width: 100%; - } - } - - /* Tabs */ - .tabs { - display: flex; - gap: 8px; - padding: 0 20px; - margin-bottom: -1px; - position: relative; - z-index: 1; - } - - .tab { - padding: 12px 24px; - cursor: pointer; - color: var(--text-secondary); - font-weight: 500; - border-radius: 16px 16px 0 0; - background: var(--bg-tertiary); - transition: all 0.2s ease; - box-shadow: - 4px -4px 8px var(--shadow-dark), - -4px -4px 8px var(--shadow-light); - } - - .tab:hover { - color: var(--text-primary); - } - - .tab.active { - color: var(--accent-color); - background: var(--bg-secondary); - box-shadow: - inset 2px 2px 4px var(--shadow-inset-dark), - inset -2px -2px 4px var(--shadow-inset-light); - } - - .tab-content { - background: var(--bg-secondary); - border-radius: 0 16px 16px 16px; - padding: 16px; - box-shadow: - inset 4px 4px 8px var(--shadow-inset-dark), - inset -4px -4px 8px var(--shadow-inset-light); - } - - .tab-panel { - display: none; - } - - .tab-panel.active { - display: block; - } - - /* Tables */ - .table-wrapper { - overflow-x: auto; - } - - table { - width: 100%; - border-collapse: collapse; - font-size: 13px; - } - - th { - padding: 12px 16px; - text-align: left; - font-weight: 600; - color: var(--text-secondary); - border-bottom: 2px solid var(--bg-tertiary); - text-transform: uppercase; - font-size: 11px; - letter-spacing: 0.5px; - } - - td { - padding: 12px 16px; - border-bottom: 1px solid var(--bg-tertiary); - color: var(--text-primary); - } - - tr:hover td { - background: var(--selection-bg); - } - - tr:last-child td { - border-bottom: none; - } - - .time { - font-variant-numeric: tabular-nums; - font-weight: 500; - } - - .percent { - font-variant-numeric: tabular-nums; - text-align: right; - color: var(--text-muted); - } - - .color-box { - display: inline-block; - width: 12px; - height: 12px; - margin-right: 8px; - vertical-align: middle; - border-radius: 4px; - box-shadow: - 2px 2px 4px var(--shadow-dark), - -2px -2px 4px var(--shadow-light); - } - - .service-badge { - display: inline-block; - font-size: 10px; - padding: 3px 10px; - background: var(--bg-tertiary); - border-radius: 12px; - color: var(--text-muted); - margin-left: 8px; - text-transform: capitalize; - font-weight: 500; - box-shadow: - inset 1px 1px 2px var(--shadow-inset-dark), - inset -1px -1px 2px var(--shadow-inset-light); - } - - .category-badge { - display: inline-block; - font-size: 10px; - padding: 3px 10px; - background: var(--bg-tertiary); - border-radius: 12px; - color: var(--accent-color); - margin-left: 4px; - text-transform: capitalize; - font-weight: 500; - box-shadow: - inset 1px 1px 2px var(--shadow-inset-dark), - inset -1px -1px 2px var(--shadow-inset-light); - } - - .no-data { - text-align: center; - padding: 40px; - color: var(--text-muted); - } - - /* Others row expansion */ - .others-row { - cursor: pointer; - } - - .others-row td:first-child::before { - content: ''; - display: inline-block; - width: 0; - height: 0; - border-left: 6px solid var(--text-muted); - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - margin-right: 8px; - transition: transform 0.2s ease; - } - - .others-row.expanded td:first-child::before { - transform: rotate(90deg); - } - - .others-detail { - display: none; - background: var(--bg-tertiary); - } - - .others-detail.visible { - display: table-row; - } - - .others-detail td { - padding-left: 32px; - } - - /* Scrollbar */ - ::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - ::-webkit-scrollbar-track { - background: var(--bg-tertiary); - border-radius: 4px; - } - - ::-webkit-scrollbar-thumb { - background: var(--text-muted); - border-radius: 4px; - } - - ::-webkit-scrollbar-thumb:hover { - background: var(--text-secondary); - } - - /* Responsive */ - @media (max-width: 600px) { - body { - padding: 12px; - } - - .stats { - gap: 12px; - } - - .stat { - padding: 16px 20px; - min-width: 100px; - } - - .stat-value { - font-size: 22px; - } - - .filter-label { - padding: 8px 14px; - font-size: 13px; - } - - .tabs { - padding: 0 12px; - } - - .tab { - padding: 10px 16px; - font-size: 13px; - } - - .theme-toggle { - width: 40px; - height: 40px; - font-size: 16px; - } - } - - /* Scroll to Top Button */ - .scroll-top { - position: fixed; - bottom: 20px; - right: 20px; - z-index: 1000; - width: 48px; - height: 48px; - border-radius: 50%; - border: none; - background: var(--bg-primary); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - box-shadow: - 6px 6px 12px var(--shadow-dark), - -6px -6px 12px var(--shadow-light); - opacity: 0; - visibility: hidden; - transition: all 0.3s ease; - } - - .scroll-top.visible { - opacity: 1; - visibility: visible; - } - - .scroll-top:hover { - transform: scale(1.05); - } - - .scroll-top:active { - box-shadow: - inset 4px 4px 8px var(--shadow-inset-dark), - inset -4px -4px 8px var(--shadow-inset-light); - } - - .scroll-top-arrow { - width: 0; - height: 0; - border-left: 8px solid transparent; - border-right: 8px solid transparent; - border-bottom: 10px solid var(--text-primary); - } - - @media (max-width: 600px) { - .scroll-top { - width: 40px; - height: 40px; - } - - .scroll-top-arrow { - border-left-width: 6px; - border-right-width: 6px; - border-bottom-width: 8px; - } - } - """ -} - def get_theme_css(style: str) -> str: - """Get the CSS for a given style.""" - return THEME_CSS.get(style, THEME_CSS["glassmorphism"]) + """Get the CSS for a given style by reading from the corresponding .css file.""" + css_file = TEMPLATES_DIR / f"{style}.css" + if css_file.exists(): + return css_file.read_text(encoding="utf-8") + # Fallback to glassmorphism if style not found + fallback = TEMPLATES_DIR / "glassmorphism.css" + return fallback.read_text(encoding="utf-8") def get_theme_config(style: str) -> str: diff --git a/templates/brutalism.css b/templates/brutalism.css new file mode 100644 index 0000000..8432b2f --- /dev/null +++ b/templates/brutalism.css @@ -0,0 +1,613 @@ +:root { + --bg-base: #ffffff; + --bg-primary: #ffffff; + --bg-secondary: #f0f0f0; + --bg-tertiary: #e0e0e0; + --text-primary: #000000; + --text-secondary: #333333; + --text-muted: #666666; + --border-color: #000000; + --border-width: 3px; + --accent-color: #ff0000; + --accent-secondary: #0000ff; + --accent-tertiary: #ffff00; + --selection-bg: #ffff00; + --selection-text: #000000; + --shadow-offset: 6px; +} + +[data-theme="dark"] { + --bg-base: #000000; + --bg-primary: #000000; + --bg-secondary: #111111; + --bg-tertiary: #222222; + --text-primary: #ffffff; + --text-secondary: #cccccc; + --text-muted: #888888; + --border-color: #ffffff; + --accent-color: #ff3333; + --accent-secondary: #3333ff; + --accent-tertiary: #ffff33; + --selection-bg: #ff3333; + --selection-text: #000000; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg-base: #000000; + --bg-primary: #000000; + --bg-secondary: #111111; + --bg-tertiary: #222222; + --text-primary: #ffffff; + --text-secondary: #cccccc; + --text-muted: #888888; + --border-color: #ffffff; + --accent-color: #ff3333; + --accent-secondary: #3333ff; + --accent-tertiary: #ffff33; + --selection-bg: #ff3333; + --selection-text: #000000; + } +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Courier New', Courier, monospace; + font-size: 14px; + max-width: 1200px; + margin: 0 auto; + padding: 20px; + background-color: var(--bg-base); + background-image: __BACKGROUND_IMAGE_CUSTOM__; + background-size: cover; + background-position: center; + background-attachment: fixed; + min-height: 100vh; + color: var(--text-primary); + line-height: 1.4; +} + +/* Theme Toggle Button */ +.theme-toggle { + position: fixed; + top: 20px; + right: 20px; + z-index: 1000; + width: 50px; + height: 50px; + border: var(--border-width) solid var(--border-color); + background: var(--bg-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border-color); + transition: transform 0.1s, box-shadow 0.1s; +} + +.theme-toggle:hover { + transform: translate(-2px, -2px); + box-shadow: calc(var(--shadow-offset) + 2px) calc(var(--shadow-offset) + 2px) 0 var(--border-color); +} + +.theme-toggle:active { + transform: translate(2px, 2px); + box-shadow: calc(var(--shadow-offset) - 2px) calc(var(--shadow-offset) - 2px) 0 var(--border-color); +} + +.theme-toggle .icon { + line-height: 1; +} + +.theme-toggle .icon-auto { + position: relative; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} + +.theme-toggle .icon-auto::before, +.theme-toggle .icon-auto::after { + position: absolute; + font-size: 20px; + line-height: 1; +} + +.theme-toggle .icon-auto::before { + content: '\2600\FE0F'; + clip-path: inset(0 50% 0 0); +} + +.theme-toggle .icon-auto::after { + content: '\D83C\DF19'; + clip-path: inset(0 0 0 50%); +} + +/* Card Style */ +.card { + background: var(--bg-primary); + border: var(--border-width) solid var(--border-color); + box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border-color); + margin-bottom: 24px; + overflow: hidden; +} + +.card-header { + padding: 12px 16px; + border-bottom: var(--border-width) solid var(--border-color); + background: var(--accent-tertiary); +} + +.card-title { + font-size: 18px; + font-weight: 900; + text-transform: uppercase; + letter-spacing: 2px; + color: var(--text-primary); +} + +[data-theme="dark"] .card-title { + color: #000000; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .card-title { + color: #000000; + } +} + +.card-content { + padding: 20px; +} + +.summaries-content { + padding: 0; + padding-top: 16px; +} + +/* Filters */ +.filters { + display: flex; + justify-content: center; + gap: 12px; + flex-wrap: wrap; +} + +.filter-label { + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + user-select: none; + padding: 8px 12px; + background: var(--bg-secondary); + border: 2px solid var(--border-color); + font-weight: 700; + text-transform: uppercase; + font-size: 12px; + transition: transform 0.1s, box-shadow 0.1s; +} + +.filter-label:hover { + transform: translate(-2px, -2px); + box-shadow: 4px 4px 0 var(--border-color); +} + +.filter-label input[type="checkbox"] { + appearance: none; + -webkit-appearance: none; + width: 20px; + height: 20px; + border: 2px solid var(--border-color); + background: var(--bg-primary); + cursor: pointer; + position: relative; +} + +.filter-label input[type="checkbox"]:checked { + background: var(--accent-color); +} + +.filter-label input[type="checkbox"]:checked::after { + content: '\2715'; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + font-size: 14px; + font-weight: 900; + color: var(--bg-primary); +} + +.filter-label .service-name { + text-transform: uppercase; + font-weight: 700; + color: var(--text-primary); +} + +.filter-label .service-count { + color: var(--text-muted); + font-size: 11px; +} + +/* Stats */ +.stats { + display: flex; + justify-content: center; + gap: 20px; + flex-wrap: wrap; +} + +.stat { + text-align: center; + padding: 20px 30px; + background: var(--bg-secondary); + border: var(--border-width) solid var(--border-color); + min-width: 150px; +} + +.stat-value { + font-size: 36px; + font-weight: 900; + color: var(--accent-color); + font-variant-numeric: tabular-nums; +} + +.stat-label { + font-size: 11px; + color: var(--text-muted); + margin-top: 4px; + text-transform: uppercase; + letter-spacing: 1px; + font-weight: 700; +} + +/* Charts */ +.charts-wrapper { + display: flex; + gap: 20px; + justify-content: center; + flex-wrap: wrap; +} + +.chart-container { + flex: 1; + min-width: 280px; + max-width: 450px; + padding: 16px; + background: var(--bg-secondary); + border: var(--border-width) solid var(--border-color); +} + +.chart-title { + font-size: 14px; + font-weight: 900; + text-align: center; + margin-bottom: 12px; + color: var(--text-primary); + text-transform: uppercase; + letter-spacing: 1px; +} + +@media (max-width: 700px) { + .charts-wrapper { + flex-direction: column; + align-items: center; + } + .chart-container { + max-width: 100%; + width: 100%; + } +} + +/* Tabs */ +.tabs { + display: flex; + gap: 0; + padding: 0 16px; + margin-bottom: -3px; + position: relative; + z-index: 1; +} + +.tab { + padding: 12px 24px; + cursor: pointer; + color: var(--text-secondary); + font-weight: 900; + text-transform: uppercase; + font-size: 12px; + letter-spacing: 1px; + background: var(--bg-tertiary); + border: var(--border-width) solid var(--border-color); + border-bottom: none; + margin-right: -3px; + transition: none; +} + +.tab:hover { + color: var(--text-primary); + background: var(--bg-secondary); +} + +.tab.active { + color: var(--accent-color); + background: var(--bg-secondary); + position: relative; +} + +.tab.active::after { + content: ''; + position: absolute; + bottom: -3px; + left: 0; + right: 0; + height: 3px; + background: var(--bg-secondary); +} + +.tab-content { + background: var(--bg-secondary); + border: var(--border-width) solid var(--border-color); + padding: 16px; +} + +.tab-panel { + display: none; +} + +.tab-panel.active { + display: block; +} + +/* Tables */ +.table-wrapper { + overflow-x: auto; +} + +table { + width: 100%; + border-collapse: collapse; + font-size: 13px; +} + +th { + padding: 12px 16px; + text-align: left; + font-weight: 900; + color: var(--text-primary); + border-bottom: var(--border-width) solid var(--border-color); + text-transform: uppercase; + font-size: 11px; + letter-spacing: 1px; + background: var(--bg-tertiary); +} + +td { + padding: 12px 16px; + border-bottom: 1px solid var(--border-color); + color: var(--text-primary); +} + +tr:hover td { + background: var(--selection-bg); + color: var(--selection-text); +} + +tr:hover .service-badge { + background: var(--border-color); + color: var(--bg-primary); +} + +tr:last-child td { + border-bottom: none; +} + +.time { + font-variant-numeric: tabular-nums; + font-weight: 700; +} + +.percent { + font-variant-numeric: tabular-nums; + text-align: right; + color: var(--text-muted); + font-weight: 700; +} + +.color-box { + display: inline-block; + width: 14px; + height: 14px; + margin-right: 8px; + vertical-align: middle; + border: 2px solid var(--border-color); +} + +.service-badge { + display: inline-block; + font-size: 10px; + padding: 2px 6px; + background: var(--bg-tertiary); + border: 1px solid var(--border-color); + color: var(--text-muted); + margin-left: 8px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.5px; +} + +.category-badge { + display: inline-block; + font-size: 10px; + padding: 2px 6px; + background: var(--accent-secondary); + border: 1px solid var(--border-color); + color: var(--bg-primary); + margin-left: 4px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.5px; +} + +.no-data { + text-align: center; + padding: 40px; + color: var(--text-muted); + font-weight: 700; + text-transform: uppercase; +} + +/* Others row expansion */ +.others-row { + cursor: pointer; +} + +.others-row td:first-child::before { + content: '\25B6'; + display: inline-block; + margin-right: 8px; + font-size: 10px; + transition: transform 0.1s; +} + +.others-row.expanded td:first-child::before { + transform: rotate(90deg); +} + +.others-detail { + display: none; + background: var(--bg-tertiary); +} + +.others-detail.visible { + display: table-row; +} + +.others-detail td { + padding-left: 32px; +} + +/* Scrollbar */ +::-webkit-scrollbar { + width: 12px; + height: 12px; +} + +::-webkit-scrollbar-track { + background: var(--bg-tertiary); + border: 1px solid var(--border-color); +} + +::-webkit-scrollbar-thumb { + background: var(--text-muted); + border: 1px solid var(--border-color); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-secondary); +} + +/* Responsive */ +@media (max-width: 600px) { + body { + padding: 12px; + } + + .stats { + gap: 12px; + } + + .stat { + padding: 16px 20px; + min-width: 100px; + } + + .stat-value { + font-size: 28px; + } + + .filter-label { + padding: 6px 10px; + font-size: 11px; + } + + .tabs { + padding: 0 12px; + } + + .tab { + padding: 10px 16px; + font-size: 11px; + } + + .theme-toggle { + width: 44px; + height: 44px; + font-size: 18px; + } +} + +/* Scroll to Top Button */ +.scroll-top { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; + width: 50px; + height: 50px; + border: var(--border-width) solid var(--border-color); + background: var(--bg-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--border-color); + opacity: 0; + visibility: hidden; + transition: opacity 0.2s, visibility 0.2s, transform 0.1s, box-shadow 0.1s; +} + +.scroll-top.visible { + opacity: 1; + visibility: visible; +} + +.scroll-top:hover { + transform: translate(-2px, -2px); + box-shadow: calc(var(--shadow-offset) + 2px) calc(var(--shadow-offset) + 2px) 0 var(--border-color); +} + +.scroll-top:active { + transform: translate(2px, 2px); + box-shadow: calc(var(--shadow-offset) - 2px) calc(var(--shadow-offset) - 2px) 0 var(--border-color); +} + +.scroll-top-arrow { + width: 0; + height: 0; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-bottom: 12px solid var(--text-primary); +} + +@media (max-width: 600px) { + .scroll-top { + width: 44px; + height: 44px; + } + + .scroll-top-arrow { + border-left-width: 8px; + border-right-width: 8px; + border-bottom-width: 10px; + } +} diff --git a/templates/glassmorphism.css b/templates/glassmorphism.css new file mode 100644 index 0000000..7d9b469 --- /dev/null +++ b/templates/glassmorphism.css @@ -0,0 +1,578 @@ +:root { + --bg-primary: rgba(255, 255, 255, 0.25); + --bg-secondary: rgba(255, 255, 255, 0.15); + --bg-tertiary: rgba(255, 255, 255, 0.1); + --text-primary: #1a1a2e; + --text-secondary: #4a4a6a; + --text-muted: #6a6a8a; + --border-color: rgba(255, 255, 255, 0.3); + --shadow-color: rgba(0, 0, 0, 0.1); + --accent-color: #6366f1; + --accent-hover: #4f46e5; + --selection-bg: rgba(99, 102, 241, 0.3); + --glass-blur: 20px; + --card-radius: 16px; +} + +[data-theme="dark"] { + --bg-primary: rgba(30, 30, 50, 0.6); + --bg-secondary: rgba(40, 40, 70, 0.5); + --bg-tertiary: rgba(50, 50, 80, 0.4); + --text-primary: #f0f0f5; + --text-secondary: #c0c0d0; + --text-muted: #9090a0; + --border-color: rgba(255, 255, 255, 0.1); + --shadow-color: rgba(0, 0, 0, 0.3); + --accent-color: #818cf8; + --accent-hover: #6366f1; + --selection-bg: rgba(129, 140, 248, 0.3); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg-primary: rgba(30, 30, 50, 0.6); + --bg-secondary: rgba(40, 40, 70, 0.5); + --bg-tertiary: rgba(50, 50, 80, 0.4); + --text-primary: #f0f0f5; + --text-secondary: #c0c0d0; + --text-muted: #9090a0; + --border-color: rgba(255, 255, 255, 0.1); + --shadow-color: rgba(0, 0, 0, 0.3); + --accent-color: #818cf8; + --accent-hover: #6366f1; + --selection-bg: rgba(129, 140, 248, 0.3); + } +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; + font-size: 14px; + max-width: 1200px; + margin: 0 auto; + padding: 20px; + background-color: #667eea; + background-image: url('__BACKGROUND_IMAGE__'); + background-size: cover; + background-position: center; + background-attachment: fixed; + min-height: 100vh; + color: var(--text-primary); + line-height: 1.5; +} + +/* Theme Toggle Button */ +.theme-toggle { + position: fixed; + top: 20px; + right: 20px; + z-index: 1000; + width: 48px; + height: 48px; + border-radius: 50%; + border: 1px solid var(--border-color); + background: var(--bg-primary); + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + box-shadow: 0 8px 32px var(--shadow-color); + transition: all 0.3s ease; +} + +.theme-toggle:hover { + transform: scale(1.1); + background: var(--bg-secondary); +} + +.theme-toggle .icon { + transition: transform 0.3s ease; +} + +.theme-toggle .icon-auto { + position: relative; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} + +.theme-toggle .icon-auto::before, +.theme-toggle .icon-auto::after { + position: absolute; + font-size: 20px; + line-height: 1; +} + +.theme-toggle .icon-auto::before { + content: '\2600\FE0F'; + clip-path: inset(0 50% 0 0); +} + +.theme-toggle .icon-auto::after { + content: '\D83C\DF19'; + clip-path: inset(0 0 0 50%); +} + +/* Card Style */ +.card { + background: var(--bg-primary); + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); + border-radius: var(--card-radius); + border: 1px solid var(--border-color); + box-shadow: 0 8px 32px var(--shadow-color); + margin-bottom: 24px; + overflow: hidden; + transition: all 0.3s ease; +} + +.card:hover { + box-shadow: 0 12px 40px var(--shadow-color); +} + +.card-header { + padding: 16px 20px; + border-bottom: 1px solid var(--border-color); + background: var(--bg-tertiary); +} + +.card-title { + font-size: 16px; + font-weight: 600; + color: var(--text-primary); +} + +.card-content { + padding: 20px; +} + +.summaries-content { + padding: 0; + padding-top: 16px; +} + +/* Filters */ +.filters { + display: flex; + justify-content: center; + gap: 16px; + flex-wrap: wrap; +} + +.filter-label { + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + user-select: none; + padding: 8px 16px; + border-radius: 24px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + transition: all 0.2s ease; +} + +.filter-label:hover { + background: var(--bg-tertiary); +} + +.filter-label input[type="checkbox"] { + appearance: none; + -webkit-appearance: none; + width: 18px; + height: 18px; + border-radius: 4px; + border: 2px solid var(--text-muted); + cursor: pointer; + position: relative; + transition: all 0.2s ease; +} + +.filter-label input[type="checkbox"]:checked { + background: var(--accent-color); + border-color: var(--accent-color); +} + +.filter-label input[type="checkbox"]:checked::after { + content: ''; + position: absolute; + left: 5px; + top: 2px; + width: 4px; + height: 8px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +.filter-label .service-name { + text-transform: capitalize; + font-weight: 500; + color: var(--text-primary); +} + +.filter-label .service-count { + color: var(--text-muted); + font-size: 12px; +} + +/* Stats */ +.stats { + display: flex; + justify-content: center; + gap: 24px; + flex-wrap: wrap; +} + +.stat { + text-align: center; + padding: 20px 32px; + background: var(--bg-secondary); + border-radius: 12px; + border: 1px solid var(--border-color); + min-width: 140px; +} + +.stat-value { + font-size: 28px; + font-weight: 700; + color: var(--accent-color); + font-variant-numeric: tabular-nums; +} + +.stat-label { + font-size: 12px; + color: var(--text-muted); + margin-top: 4px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Charts */ +.charts-wrapper { + display: flex; + gap: 24px; + justify-content: center; + flex-wrap: wrap; +} + +.chart-container { + flex: 1; + min-width: 280px; + max-width: 450px; + padding: 16px; + background: var(--bg-secondary); + border-radius: 12px; + border: 1px solid var(--border-color); +} + +.chart-title { + font-size: 14px; + font-weight: 600; + text-align: center; + margin-bottom: 12px; + color: var(--text-primary); +} + +@media (max-width: 700px) { + .charts-wrapper { + flex-direction: column; + align-items: center; + } + .chart-container { + max-width: 100%; + width: 100%; + } +} + +/* Tabs */ +.tabs { + display: flex; + gap: 8px; + padding: 0 20px; + margin-bottom: -1px; + position: relative; + z-index: 1; +} + +.tab { + padding: 12px 24px; + cursor: pointer; + color: var(--text-secondary); + font-weight: 500; + border-radius: 12px 12px 0 0; + background: var(--bg-tertiary); + border: 1px solid transparent; + border-bottom: none; + transition: all 0.2s ease; +} + +.tab:hover { + color: var(--text-primary); + background: var(--bg-secondary); +} + +.tab.active { + color: var(--accent-color); + background: var(--bg-secondary); + border-color: var(--border-color); +} + +.tab-content { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 0 12px 12px 12px; + padding: 16px; +} + +.tab-panel { + display: none; +} + +.tab-panel.active { + display: block; +} + +/* Tables */ +.table-wrapper { + overflow-x: auto; +} + +table { + width: 100%; + border-collapse: collapse; + font-size: 13px; +} + +th { + padding: 12px 16px; + text-align: left; + font-weight: 600; + color: var(--text-secondary); + border-bottom: 2px solid var(--border-color); + text-transform: uppercase; + font-size: 11px; + letter-spacing: 0.5px; +} + +td { + padding: 12px 16px; + border-bottom: 1px solid var(--border-color); + color: var(--text-primary); +} + +tr:hover td { + background: var(--selection-bg); +} + +tr:last-child td { + border-bottom: none; +} + +.time { + font-variant-numeric: tabular-nums; + font-weight: 500; +} + +.percent { + font-variant-numeric: tabular-nums; + text-align: right; + color: var(--text-muted); +} + +.color-box { + display: inline-block; + width: 12px; + height: 12px; + margin-right: 8px; + vertical-align: middle; + border-radius: 3px; +} + +.service-badge { + display: inline-block; + font-size: 10px; + padding: 2px 8px; + background: var(--bg-tertiary); + border-radius: 12px; + color: var(--text-muted); + margin-left: 8px; + text-transform: capitalize; + font-weight: 500; +} + +.category-badge { + display: inline-block; + font-size: 10px; + padding: 2px 8px; + background: rgba(99, 102, 241, 0.2); + border-radius: 12px; + color: var(--accent-color); + margin-left: 4px; + text-transform: capitalize; + font-weight: 500; +} + +.no-data { + text-align: center; + padding: 40px; + color: var(--text-muted); +} + +/* Others row expansion */ +.others-row { + cursor: pointer; +} + +.others-row td:first-child::before { + content: ''; + display: inline-block; + width: 0; + height: 0; + border-left: 6px solid var(--text-muted); + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + margin-right: 8px; + transition: transform 0.2s ease; +} + +.others-row.expanded td:first-child::before { + transform: rotate(90deg); +} + +.others-detail { + display: none; + background: var(--bg-tertiary); +} + +.others-detail.visible { + display: table-row; +} + +.others-detail td { + padding-left: 32px; +} + +/* Scrollbar */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--bg-tertiary); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: var(--text-muted); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-secondary); +} + +/* Responsive */ +@media (max-width: 600px) { + body { + padding: 12px; + } + + .stats { + gap: 12px; + } + + .stat { + padding: 16px 20px; + min-width: 100px; + } + + .stat-value { + font-size: 22px; + } + + .filter-label { + padding: 6px 12px; + font-size: 13px; + } + + .tabs { + padding: 0 12px; + } + + .tab { + padding: 10px 16px; + font-size: 13px; + } + + .theme-toggle { + width: 40px; + height: 40px; + font-size: 16px; + } +} + +/* Scroll to Top Button */ +.scroll-top { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; + width: 48px; + height: 48px; + border-radius: 50%; + border: 1px solid var(--border-color); + background: var(--bg-primary); + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 8px 32px var(--shadow-color); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.scroll-top.visible { + opacity: 1; + visibility: visible; +} + +.scroll-top:hover { + transform: scale(1.1); + background: var(--bg-secondary); +} + +.scroll-top-arrow { + width: 0; + height: 0; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-bottom: 10px solid var(--text-primary); +} + +@media (max-width: 600px) { + .scroll-top { + width: 40px; + height: 40px; + } + + .scroll-top-arrow { + border-left-width: 6px; + border-right-width: 6px; + border-bottom-width: 8px; + } +} diff --git a/templates/neumorphism.css b/templates/neumorphism.css new file mode 100644 index 0000000..51644d3 --- /dev/null +++ b/templates/neumorphism.css @@ -0,0 +1,618 @@ +:root { + --bg-base: #e0e5ec; + --bg-primary: #e0e5ec; + --bg-secondary: #e0e5ec; + --bg-tertiary: #d1d9e6; + --text-primary: #2d3436; + --text-secondary: #4a5568; + --text-muted: #718096; + --accent-color: #6366f1; + --accent-hover: #4f46e5; + --selection-bg: rgba(99, 102, 241, 0.15); + --shadow-light: rgba(255, 255, 255, 0.8); + --shadow-dark: rgba(163, 177, 198, 0.6); + --shadow-inset-light: rgba(255, 255, 255, 0.7); + --shadow-inset-dark: rgba(163, 177, 198, 0.5); + --card-radius: 20px; + --border-color: transparent; +} + +[data-theme="dark"] { + --bg-base: #2d3436; + --bg-primary: #2d3436; + --bg-secondary: #2d3436; + --bg-tertiary: #353b3d; + --text-primary: #f0f0f5; + --text-secondary: #b0b8c0; + --text-muted: #8090a0; + --accent-color: #818cf8; + --accent-hover: #6366f1; + --selection-bg: rgba(129, 140, 248, 0.2); + --shadow-light: rgba(255, 255, 255, 0.05); + --shadow-dark: rgba(0, 0, 0, 0.4); + --shadow-inset-light: rgba(255, 255, 255, 0.03); + --shadow-inset-dark: rgba(0, 0, 0, 0.3); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg-base: #2d3436; + --bg-primary: #2d3436; + --bg-secondary: #2d3436; + --bg-tertiary: #353b3d; + --text-primary: #f0f0f5; + --text-secondary: #b0b8c0; + --text-muted: #8090a0; + --accent-color: #818cf8; + --accent-hover: #6366f1; + --selection-bg: rgba(129, 140, 248, 0.2); + --shadow-light: rgba(255, 255, 255, 0.05); + --shadow-dark: rgba(0, 0, 0, 0.4); + --shadow-inset-light: rgba(255, 255, 255, 0.03); + --shadow-inset-dark: rgba(0, 0, 0, 0.3); + } +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; + font-size: 14px; + max-width: 1200px; + margin: 0 auto; + padding: 20px; + background-color: var(--bg-base); + min-height: 100vh; + color: var(--text-primary); + line-height: 1.5; +} + +/* Theme Toggle Button */ +.theme-toggle { + position: fixed; + top: 20px; + right: 20px; + z-index: 1000; + width: 48px; + height: 48px; + border-radius: 50%; + border: none; + background: var(--bg-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + box-shadow: + 6px 6px 12px var(--shadow-dark), + -6px -6px 12px var(--shadow-light); + transition: all 0.3s ease; +} + +.theme-toggle:hover { + transform: scale(1.05); +} + +.theme-toggle:active { + box-shadow: + inset 4px 4px 8px var(--shadow-inset-dark), + inset -4px -4px 8px var(--shadow-inset-light); +} + +.theme-toggle .icon { + transition: transform 0.3s ease; +} + +.theme-toggle .icon-auto { + position: relative; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; +} + +.theme-toggle .icon-auto::before, +.theme-toggle .icon-auto::after { + position: absolute; + font-size: 20px; + line-height: 1; +} + +.theme-toggle .icon-auto::before { + content: '\2600\FE0F'; + clip-path: inset(0 50% 0 0); +} + +.theme-toggle .icon-auto::after { + content: '\D83C\DF19'; + clip-path: inset(0 0 0 50%); +} + +/* Card Style */ +.card { + background: var(--bg-primary); + border-radius: var(--card-radius); + box-shadow: + 8px 8px 16px var(--shadow-dark), + -8px -8px 16px var(--shadow-light); + margin-bottom: 24px; + overflow: hidden; + transition: all 0.3s ease; +} + +.card-header { + padding: 16px 20px; + background: var(--bg-tertiary); + border-radius: var(--card-radius) var(--card-radius) 0 0; +} + +.card-title { + font-size: 16px; + font-weight: 600; + color: var(--text-primary); +} + +.card-content { + padding: 20px; +} + +.summaries-content { + padding: 0; + padding-top: 16px; +} + +/* Filters */ +.filters { + display: flex; + justify-content: center; + gap: 16px; + flex-wrap: wrap; +} + +.filter-label { + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + user-select: none; + padding: 10px 18px; + border-radius: 30px; + background: var(--bg-secondary); + box-shadow: + 4px 4px 8px var(--shadow-dark), + -4px -4px 8px var(--shadow-light); + transition: all 0.2s ease; +} + +.filter-label:hover { + transform: translateY(-2px); +} + +.filter-label:active { + box-shadow: + inset 3px 3px 6px var(--shadow-inset-dark), + inset -3px -3px 6px var(--shadow-inset-light); +} + +.filter-label input[type="checkbox"] { + appearance: none; + -webkit-appearance: none; + width: 20px; + height: 20px; + border-radius: 6px; + background: var(--bg-secondary); + cursor: pointer; + position: relative; + transition: all 0.2s ease; + box-shadow: + inset 2px 2px 4px var(--shadow-inset-dark), + inset -2px -2px 4px var(--shadow-inset-light); +} + +.filter-label input[type="checkbox"]:checked { + background: var(--accent-color); + box-shadow: + inset 2px 2px 4px rgba(0, 0, 0, 0.2), + inset -2px -2px 4px rgba(255, 255, 255, 0.1); +} + +.filter-label input[type="checkbox"]:checked::after { + content: ''; + position: absolute; + left: 6px; + top: 3px; + width: 5px; + height: 9px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +.filter-label .service-name { + text-transform: capitalize; + font-weight: 500; + color: var(--text-primary); +} + +.filter-label .service-count { + color: var(--text-muted); + font-size: 12px; +} + +/* Stats */ +.stats { + display: flex; + justify-content: center; + gap: 24px; + flex-wrap: wrap; +} + +.stat { + text-align: center; + padding: 24px 36px; + background: var(--bg-secondary); + border-radius: 16px; + min-width: 140px; + box-shadow: + 6px 6px 12px var(--shadow-dark), + -6px -6px 12px var(--shadow-light); +} + +.stat-value { + font-size: 28px; + font-weight: 700; + color: var(--accent-color); + font-variant-numeric: tabular-nums; +} + +.stat-label { + font-size: 12px; + color: var(--text-muted); + margin-top: 4px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* Charts */ +.charts-wrapper { + display: flex; + gap: 24px; + justify-content: center; + flex-wrap: wrap; +} + +.chart-container { + flex: 1; + min-width: 280px; + max-width: 450px; + padding: 20px; + background: var(--bg-secondary); + border-radius: 16px; + box-shadow: + inset 4px 4px 8px var(--shadow-inset-dark), + inset -4px -4px 8px var(--shadow-inset-light); +} + +.chart-title { + font-size: 14px; + font-weight: 600; + text-align: center; + margin-bottom: 12px; + color: var(--text-primary); +} + +@media (max-width: 700px) { + .charts-wrapper { + flex-direction: column; + align-items: center; + } + .chart-container { + max-width: 100%; + width: 100%; + } +} + +/* Tabs */ +.tabs { + display: flex; + gap: 8px; + padding: 0 20px; + margin-bottom: -1px; + position: relative; + z-index: 1; +} + +.tab { + padding: 12px 24px; + cursor: pointer; + color: var(--text-secondary); + font-weight: 500; + border-radius: 16px 16px 0 0; + background: var(--bg-tertiary); + transition: all 0.2s ease; + box-shadow: + 4px -4px 8px var(--shadow-dark), + -4px -4px 8px var(--shadow-light); +} + +.tab:hover { + color: var(--text-primary); +} + +.tab.active { + color: var(--accent-color); + background: var(--bg-secondary); + box-shadow: + inset 2px 2px 4px var(--shadow-inset-dark), + inset -2px -2px 4px var(--shadow-inset-light); +} + +.tab-content { + background: var(--bg-secondary); + border-radius: 0 16px 16px 16px; + padding: 16px; + box-shadow: + inset 4px 4px 8px var(--shadow-inset-dark), + inset -4px -4px 8px var(--shadow-inset-light); +} + +.tab-panel { + display: none; +} + +.tab-panel.active { + display: block; +} + +/* Tables */ +.table-wrapper { + overflow-x: auto; +} + +table { + width: 100%; + border-collapse: collapse; + font-size: 13px; +} + +th { + padding: 12px 16px; + text-align: left; + font-weight: 600; + color: var(--text-secondary); + border-bottom: 2px solid var(--bg-tertiary); + text-transform: uppercase; + font-size: 11px; + letter-spacing: 0.5px; +} + +td { + padding: 12px 16px; + border-bottom: 1px solid var(--bg-tertiary); + color: var(--text-primary); +} + +tr:hover td { + background: var(--selection-bg); +} + +tr:last-child td { + border-bottom: none; +} + +.time { + font-variant-numeric: tabular-nums; + font-weight: 500; +} + +.percent { + font-variant-numeric: tabular-nums; + text-align: right; + color: var(--text-muted); +} + +.color-box { + display: inline-block; + width: 12px; + height: 12px; + margin-right: 8px; + vertical-align: middle; + border-radius: 4px; + box-shadow: + 2px 2px 4px var(--shadow-dark), + -2px -2px 4px var(--shadow-light); +} + +.service-badge { + display: inline-block; + font-size: 10px; + padding: 3px 10px; + background: var(--bg-tertiary); + border-radius: 12px; + color: var(--text-muted); + margin-left: 8px; + text-transform: capitalize; + font-weight: 500; + box-shadow: + inset 1px 1px 2px var(--shadow-inset-dark), + inset -1px -1px 2px var(--shadow-inset-light); +} + +.category-badge { + display: inline-block; + font-size: 10px; + padding: 3px 10px; + background: var(--bg-tertiary); + border-radius: 12px; + color: var(--accent-color); + margin-left: 4px; + text-transform: capitalize; + font-weight: 500; + box-shadow: + inset 1px 1px 2px var(--shadow-inset-dark), + inset -1px -1px 2px var(--shadow-inset-light); +} + +.no-data { + text-align: center; + padding: 40px; + color: var(--text-muted); +} + +/* Others row expansion */ +.others-row { + cursor: pointer; +} + +.others-row td:first-child::before { + content: ''; + display: inline-block; + width: 0; + height: 0; + border-left: 6px solid var(--text-muted); + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + margin-right: 8px; + transition: transform 0.2s ease; +} + +.others-row.expanded td:first-child::before { + transform: rotate(90deg); +} + +.others-detail { + display: none; + background: var(--bg-tertiary); +} + +.others-detail.visible { + display: table-row; +} + +.others-detail td { + padding-left: 32px; +} + +/* Scrollbar */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--bg-tertiary); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: var(--text-muted); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-secondary); +} + +/* Responsive */ +@media (max-width: 600px) { + body { + padding: 12px; + } + + .stats { + gap: 12px; + } + + .stat { + padding: 16px 20px; + min-width: 100px; + } + + .stat-value { + font-size: 22px; + } + + .filter-label { + padding: 8px 14px; + font-size: 13px; + } + + .tabs { + padding: 0 12px; + } + + .tab { + padding: 10px 16px; + font-size: 13px; + } + + .theme-toggle { + width: 40px; + height: 40px; + font-size: 16px; + } +} + +/* Scroll to Top Button */ +.scroll-top { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; + width: 48px; + height: 48px; + border-radius: 50%; + border: none; + background: var(--bg-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + box-shadow: + 6px 6px 12px var(--shadow-dark), + -6px -6px 12px var(--shadow-light); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.scroll-top.visible { + opacity: 1; + visibility: visible; +} + +.scroll-top:hover { + transform: scale(1.05); +} + +.scroll-top:active { + box-shadow: + inset 4px 4px 8px var(--shadow-inset-dark), + inset -4px -4px 8px var(--shadow-inset-light); +} + +.scroll-top-arrow { + width: 0; + height: 0; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-bottom: 10px solid var(--text-primary); +} + +@media (max-width: 600px) { + .scroll-top { + width: 40px; + height: 40px; + } + + .scroll-top-arrow { + border-left-width: 6px; + border-right-width: 6px; + border-bottom-width: 8px; + } +}