Upload folder using huggingface_hub
Browse files- src/pdf_report.py +4 -4
- static/index.html +1 -1
- static/style.css +25 -25
src/pdf_report.py
CHANGED
|
@@ -11,13 +11,13 @@ from reportlab.lib.styles import ParagraphStyle
|
|
| 11 |
from reportlab.lib.units import mm
|
| 12 |
from reportlab.platypus import (Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle)
|
| 13 |
|
| 14 |
-
INK = colors.HexColor("#
|
| 15 |
-
BRASS = colors.HexColor("#
|
| 16 |
-
DIM = colors.HexColor("#
|
| 17 |
GOOD = colors.HexColor("#1e7a4a")
|
| 18 |
WARN = colors.HexColor("#9a6b12")
|
| 19 |
BAD = colors.HexColor("#a33a40")
|
| 20 |
-
LINE = colors.HexColor("#
|
| 21 |
|
| 22 |
_ACTION_COLOR = {"Buy More": GOOD, "Keep": DIM, "Trim": WARN, "Sell": BAD}
|
| 23 |
_SEV_COLOR = {"good": GOOD, "warn": WARN, "bad": BAD, "OK": GOOD, "WATCH": WARN, "ALERT": BAD}
|
|
|
|
| 11 |
from reportlab.lib.units import mm
|
| 12 |
from reportlab.platypus import (Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle)
|
| 13 |
|
| 14 |
+
INK = colors.HexColor("#131a22")
|
| 15 |
+
BRASS = colors.HexColor("#0b7fa6") # brand accent (print-safe deep cyan)
|
| 16 |
+
DIM = colors.HexColor("#5c6a7a")
|
| 17 |
GOOD = colors.HexColor("#1e7a4a")
|
| 18 |
WARN = colors.HexColor("#9a6b12")
|
| 19 |
BAD = colors.HexColor("#a33a40")
|
| 20 |
+
LINE = colors.HexColor("#ccd6e0")
|
| 21 |
|
| 22 |
_ACTION_COLOR = {"Buy More": GOOD, "Keep": DIM, "Trim": WARN, "Sell": BAD}
|
| 23 |
_SEV_COLOR = {"good": GOOD, "warn": WARN, "bad": BAD, "OK": GOOD, "WATCH": WARN, "ALERT": BAD}
|
static/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
<meta charset="utf-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
<title>Conclave</title>
|
| 7 |
-
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><circle cx='16' cy='16' r='4' fill='%
|
| 8 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
|
|
| 4 |
<meta charset="utf-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
<title>Conclave</title>
|
| 7 |
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><circle cx='16' cy='16' r='4' fill='%2338dbff'/><g fill='%23e8eef6'><circle cx='16' cy='5' r='2.4'/><circle cx='26.5' cy='12.6' r='2.4'/><circle cx='22.5' cy='25' r='2.4'/><circle cx='9.5' cy='25' r='2.4'/><circle cx='5.5' cy='12.6' r='2.4'/></g></svg>">
|
| 8 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
static/style.css
CHANGED
|
@@ -1,15 +1,15 @@
|
|
| 1 |
-
/* Conclave β midnight
|
| 2 |
:root {
|
| 3 |
-
--bg: #
|
| 4 |
-
--text: #
|
| 5 |
-
--good: #
|
| 6 |
font-size: 16px;
|
| 7 |
}
|
| 8 |
* { box-sizing: border-box; margin: 0; padding: 0; }
|
| 9 |
body {
|
| 10 |
background:
|
| 11 |
-
radial-gradient(1100px 520px at 75% -12%, #
|
| 12 |
-
radial-gradient(900px 500px at -10% 8%, #
|
| 13 |
color: var(--text); font-family: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;
|
| 14 |
min-height: 100vh; padding-bottom: 48px;
|
| 15 |
}
|
|
@@ -25,10 +25,10 @@ h2 { font-size: .92rem; margin-bottom: 12px; color: var(--accent2); text-transfo
|
|
| 25 |
.sub { color: var(--dim); margin-bottom: 18px; }
|
| 26 |
button, .primary { font: inherit; cursor: pointer; border-radius: 10px; }
|
| 27 |
.primary {
|
| 28 |
-
display: inline-block; background: linear-gradient(135deg, #
|
| 29 |
-
color: #
|
| 30 |
letter-spacing: .01em; text-decoration: none; text-align: center; transition: filter .15s;
|
| 31 |
-
box-shadow: 0 2px
|
| 32 |
}
|
| 33 |
.primary:hover { filter: brightness(1.1); }
|
| 34 |
.ghost { background: transparent; border: 1px solid var(--line); color: var(--dim); padding: 8px 14px; }
|
|
@@ -60,7 +60,7 @@ button, .primary { font: inherit; cursor: pointer; border-radius: 10px; }
|
|
| 60 |
#code-input { background: var(--panel2); color: var(--text); border: 1px solid var(--line); border-radius: 8px; padding: 8px 12px; font: inherit; width: 140px; letter-spacing: .12em; }
|
| 61 |
#code-input:focus { outline: none; border-color: var(--accent); }
|
| 62 |
.code-status { color: var(--dim); font-size: .8rem; }
|
| 63 |
-
.watch-card { border-color: #
|
| 64 |
.track-head { color: var(--dim); font-size: .85rem; margin-bottom: 10px; }
|
| 65 |
.track-pill { display: inline-block; border: 1px solid var(--line); border-radius: 999px; padding: 4px 12px; font-size: .84rem; margin: 0 6px 8px 0; color: var(--text); }
|
| 66 |
.alerts { margin-top: 6px; }
|
|
@@ -87,12 +87,12 @@ button, .primary { font: inherit; cursor: pointer; border-radius: 10px; }
|
|
| 87 |
.progress-card { text-align: center; padding: 40px 32px; }
|
| 88 |
#progress-caption { color: var(--text); font-size: 1.1rem; font-weight: 500; text-transform: none; letter-spacing: 0; margin-bottom: 20px; min-height: 1.4em; }
|
| 89 |
.bar-track { height: 10px; background: var(--panel2); border-radius: 6px; overflow: hidden; margin-bottom: 22px; }
|
| 90 |
-
.bar-fill { height: 100%; width: 0%; border-radius: 6px; background: linear-gradient(90deg, #
|
| 91 |
.chips { display: flex; justify-content: center; flex-wrap: wrap; gap: 8px; margin-bottom: 30px; }
|
| 92 |
.chip { border: 1px solid var(--line); color: var(--dim); border-radius: 999px; padding: 5px 13px; font-size: .82rem; transition: all .3s; }
|
| 93 |
-
.chip.lit { border-color: var(--accent); color: var(--text); background: #
|
| 94 |
.skeleton { opacity: .5; }
|
| 95 |
-
.sk { background: linear-gradient(90deg, var(--panel2), #
|
| 96 |
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
| 97 |
.sk-hero { height: 70px; margin-bottom: 12px; }
|
| 98 |
.sk-row { display: flex; gap: 12px; margin-bottom: 12px; }
|
|
@@ -101,17 +101,17 @@ button, .primary { font: inherit; cursor: pointer; border-radius: 10px; }
|
|
| 101 |
|
| 102 |
/* ββ hero ββ */
|
| 103 |
.hero { display: flex; gap: 26px; align-items: center; flex-wrap: wrap;
|
| 104 |
-
border-top: 2px solid #
|
| 105 |
#gauge text { fill: var(--text); font-family: "Fraunces", Georgia, serif; font-size: 40px; font-weight: 700; }
|
| 106 |
#gauge .gauge-label { font-size: 11px; fill: var(--dim); font-family: "Inter", sans-serif; font-weight: 400; letter-spacing: .08em; text-transform: uppercase; }
|
| 107 |
.hero-text { flex: 1; min-width: 260px; }
|
| 108 |
.verdict { font-family: "Fraunces", Georgia, serif; font-size: 1.3rem; font-weight: 500; line-height: 1.45; margin-bottom: 14px; }
|
| 109 |
.action-strip { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
| 110 |
.pill { border-radius: 999px; padding: 5px 14px; font-size: .85rem; font-weight: 600; }
|
| 111 |
-
.pill.buy { background: #
|
| 112 |
-
.pill.keep { background: #
|
| 113 |
-
.pill.trim { background: #
|
| 114 |
-
.pill.sell { background: #
|
| 115 |
.pill.pl { background: var(--panel2); border: 1px solid var(--line); color: var(--text); }
|
| 116 |
|
| 117 |
/* ββ highlights ββ */
|
|
@@ -133,10 +133,10 @@ button, .primary { font: inherit; cursor: pointer; border-radius: 10px; }
|
|
| 133 |
.tick b { font-size: .98rem; } .tick span { display: block; color: var(--dim); font-size: .78rem; }
|
| 134 |
.up { color: var(--good); } .dn { color: var(--bad); }
|
| 135 |
.badge { display: inline-block; border-radius: 7px; padding: 3px 10px; font-weight: 700; font-size: .8rem; white-space: nowrap; }
|
| 136 |
-
.badge.buy { background: #
|
| 137 |
-
.badge.keep { background: #
|
| 138 |
-
.badge.trim { background: #
|
| 139 |
-
.badge.sell { background: #
|
| 140 |
.why { color: var(--dim); font-size: .86rem; max-width: 340px; line-height: 1.4; }
|
| 141 |
tr.detail td { background: var(--panel2); padding: 16px 18px; }
|
| 142 |
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
|
|
@@ -168,9 +168,9 @@ tr.detail td { background: var(--panel2); padding: 16px 18px; }
|
|
| 168 |
.scorecard { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 10px; }
|
| 169 |
.sc { display: flex; gap: 10px; background: var(--panel2); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; font-size: .87rem; }
|
| 170 |
.sc .st { font-weight: 700; font-size: .72rem; padding: 2px 8px; border-radius: 6px; height: fit-content; }
|
| 171 |
-
.st.OK { background: #
|
| 172 |
-
.st.WATCH { background: #
|
| 173 |
-
.st.ALERT { background: #
|
| 174 |
.sc div p { color: var(--dim); margin-top: 3px; line-height: 1.4; }
|
| 175 |
.strategy-read { margin-top: 14px; color: var(--dim); font-size: .9rem; line-height: 1.5; }
|
| 176 |
.strategy-read b { color: var(--text); }
|
|
|
|
| 1 |
+
/* Conclave β midnight terminal: ink navy, cool white text, neon-cyan verdicts */
|
| 2 |
:root {
|
| 3 |
+
--bg: #0a0e15; --panel: #10161f; --panel2: #16202c; --line: #243247;
|
| 4 |
+
--text: #e8eef6; --dim: #8b98ab; --accent: #38dbff; --accent2: #8ae9ff;
|
| 5 |
+
--good: #2fd98a; --warn: #e8a838; --bad: #ff6b74;
|
| 6 |
font-size: 16px;
|
| 7 |
}
|
| 8 |
* { box-sizing: border-box; margin: 0; padding: 0; }
|
| 9 |
body {
|
| 10 |
background:
|
| 11 |
+
radial-gradient(1100px 520px at 75% -12%, #38dbff12, transparent),
|
| 12 |
+
radial-gradient(900px 500px at -10% 8%, #16233a40, transparent), var(--bg);
|
| 13 |
color: var(--text); font-family: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;
|
| 14 |
min-height: 100vh; padding-bottom: 48px;
|
| 15 |
}
|
|
|
|
| 25 |
.sub { color: var(--dim); margin-bottom: 18px; }
|
| 26 |
button, .primary { font: inherit; cursor: pointer; border-radius: 10px; }
|
| 27 |
.primary {
|
| 28 |
+
display: inline-block; background: linear-gradient(135deg, #7be8ff, var(--accent) 55%, #16a4cd);
|
| 29 |
+
color: #04121a; border: none; padding: 12px 28px; font-weight: 700; font-size: 1rem;
|
| 30 |
letter-spacing: .01em; text-decoration: none; text-align: center; transition: filter .15s;
|
| 31 |
+
box-shadow: 0 2px 16px #38dbff40;
|
| 32 |
}
|
| 33 |
.primary:hover { filter: brightness(1.1); }
|
| 34 |
.ghost { background: transparent; border: 1px solid var(--line); color: var(--dim); padding: 8px 14px; }
|
|
|
|
| 60 |
#code-input { background: var(--panel2); color: var(--text); border: 1px solid var(--line); border-radius: 8px; padding: 8px 12px; font: inherit; width: 140px; letter-spacing: .12em; }
|
| 61 |
#code-input:focus { outline: none; border-color: var(--accent); }
|
| 62 |
.code-status { color: var(--dim); font-size: .8rem; }
|
| 63 |
+
.watch-card { border-color: #38dbff40; }
|
| 64 |
.track-head { color: var(--dim); font-size: .85rem; margin-bottom: 10px; }
|
| 65 |
.track-pill { display: inline-block; border: 1px solid var(--line); border-radius: 999px; padding: 4px 12px; font-size: .84rem; margin: 0 6px 8px 0; color: var(--text); }
|
| 66 |
.alerts { margin-top: 6px; }
|
|
|
|
| 87 |
.progress-card { text-align: center; padding: 40px 32px; }
|
| 88 |
#progress-caption { color: var(--text); font-size: 1.1rem; font-weight: 500; text-transform: none; letter-spacing: 0; margin-bottom: 20px; min-height: 1.4em; }
|
| 89 |
.bar-track { height: 10px; background: var(--panel2); border-radius: 6px; overflow: hidden; margin-bottom: 22px; }
|
| 90 |
+
.bar-fill { height: 100%; width: 0%; border-radius: 6px; background: linear-gradient(90deg, #16a4cd, var(--accent), #a5efff); transition: width .6s ease; }
|
| 91 |
.chips { display: flex; justify-content: center; flex-wrap: wrap; gap: 8px; margin-bottom: 30px; }
|
| 92 |
.chip { border: 1px solid var(--line); color: var(--dim); border-radius: 999px; padding: 5px 13px; font-size: .82rem; transition: all .3s; }
|
| 93 |
+
.chip.lit { border-color: var(--accent); color: var(--text); background: #38dbff18; box-shadow: 0 0 10px #38dbff22; }
|
| 94 |
.skeleton { opacity: .5; }
|
| 95 |
+
.sk { background: linear-gradient(90deg, var(--panel2), #1d2b3c, var(--panel2)); background-size: 200% 100%; animation: shimmer 1.6s infinite; border-radius: 10px; }
|
| 96 |
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
| 97 |
.sk-hero { height: 70px; margin-bottom: 12px; }
|
| 98 |
.sk-row { display: flex; gap: 12px; margin-bottom: 12px; }
|
|
|
|
| 101 |
|
| 102 |
/* ββ hero ββ */
|
| 103 |
.hero { display: flex; gap: 26px; align-items: center; flex-wrap: wrap;
|
| 104 |
+
border-top: 2px solid #38dbff55; }
|
| 105 |
#gauge text { fill: var(--text); font-family: "Fraunces", Georgia, serif; font-size: 40px; font-weight: 700; }
|
| 106 |
#gauge .gauge-label { font-size: 11px; fill: var(--dim); font-family: "Inter", sans-serif; font-weight: 400; letter-spacing: .08em; text-transform: uppercase; }
|
| 107 |
.hero-text { flex: 1; min-width: 260px; }
|
| 108 |
.verdict { font-family: "Fraunces", Georgia, serif; font-size: 1.3rem; font-weight: 500; line-height: 1.45; margin-bottom: 14px; }
|
| 109 |
.action-strip { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
| 110 |
.pill { border-radius: 999px; padding: 5px 14px; font-size: .85rem; font-weight: 600; }
|
| 111 |
+
.pill.buy { background: #2fd98a22; color: var(--good); border: 1px solid #2fd98a55; }
|
| 112 |
+
.pill.keep { background: #8fa5bd1c; color: #bccbde; border: 1px solid #8fa5bd44; }
|
| 113 |
+
.pill.trim { background: #e8a83822; color: var(--warn); border: 1px solid #e8a83855; }
|
| 114 |
+
.pill.sell { background: #ff6b7422; color: var(--bad); border: 1px solid #ff6b7455; }
|
| 115 |
.pill.pl { background: var(--panel2); border: 1px solid var(--line); color: var(--text); }
|
| 116 |
|
| 117 |
/* ββ highlights ββ */
|
|
|
|
| 133 |
.tick b { font-size: .98rem; } .tick span { display: block; color: var(--dim); font-size: .78rem; }
|
| 134 |
.up { color: var(--good); } .dn { color: var(--bad); }
|
| 135 |
.badge { display: inline-block; border-radius: 7px; padding: 3px 10px; font-weight: 700; font-size: .8rem; white-space: nowrap; }
|
| 136 |
+
.badge.buy { background: #2fd98a25; color: var(--good); }
|
| 137 |
+
.badge.keep { background: #8fa5bd20; color: #bccbde; }
|
| 138 |
+
.badge.trim { background: #e8a83825; color: var(--warn); }
|
| 139 |
+
.badge.sell { background: #ff6b7425; color: var(--bad); }
|
| 140 |
.why { color: var(--dim); font-size: .86rem; max-width: 340px; line-height: 1.4; }
|
| 141 |
tr.detail td { background: var(--panel2); padding: 16px 18px; }
|
| 142 |
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
|
|
|
|
| 168 |
.scorecard { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 10px; }
|
| 169 |
.sc { display: flex; gap: 10px; background: var(--panel2); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; font-size: .87rem; }
|
| 170 |
.sc .st { font-weight: 700; font-size: .72rem; padding: 2px 8px; border-radius: 6px; height: fit-content; }
|
| 171 |
+
.st.OK { background: #2fd98a25; color: var(--good); }
|
| 172 |
+
.st.WATCH { background: #e8a83825; color: var(--warn); }
|
| 173 |
+
.st.ALERT { background: #ff6b7425; color: var(--bad); }
|
| 174 |
.sc div p { color: var(--dim); margin-top: 3px; line-height: 1.4; }
|
| 175 |
.strategy-read { margin-top: 14px; color: var(--dim); font-size: .9rem; line-height: 1.5; }
|
| 176 |
.strategy-read b { color: var(--text); }
|