Burnmydays commited on
Commit
22eb036
·
2 Parent(s): 15a55685079cfa

Merge branch 'main' of https://github.com/Burnmydays/hf-

Browse files
Files changed (3) hide show
  1. SCRATCHPAD.md +15 -0
  2. app.py +77 -15
  3. theme.py +96 -6
SCRATCHPAD.md CHANGED
@@ -40,6 +40,21 @@ cd /Users/dericmchenry/Desktop/moses-sigrank
40
 
41
  ## WORK LOG (newest first)
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  ### Devin session — 2026-06-15
44
  - [done] GPU FIX — applied `@GPU` decorator to `narrate()` in narrate.py (line 60).
45
  ZeroGPU needs the inference function decorated or GPU alloc never happens and the
 
40
 
41
  ## WORK LOG (newest first)
42
 
43
+ ### Devin session — 2026-06-15 (visual upgrade)
44
+ - [done] theme.py CSS upgrade — brighter default bar gradient (0.28→0.72), `.mb-row.rank1`
45
+ gold left-border + dominant #1 row, colored rank numbers (`.mb-rank-1/2/3`), hero upgrade
46
+ (32px title, `#moses-stat-strip`), `.mb-raw` color #5f573f→#7a7060, `.comp-bar` composition
47
+ bar, full `.sig-card` trading-card styles, and a `@media (max-width:700px)` mobile rule that
48
+ hides velocity/leverage columns.
49
+ - [done] app.py UI upgrade — hero stat strip HTML; `board_html()` now adds `rank1` class to row 1
50
+ and `mb-rank-{1,2,3}` spans for the top 3; new `comp_bar_html(c)` and `card_html(name,m,rank,
51
+ total_ops,narration_text)` helpers (+ `_first_sentence` quote truncation); `profile_md` drops the
52
+ text composition line and accepts an optional `read` so narrate() runs once; "Share card" section
53
+ (gr.HTML) added to Measure-yourself tab; `run_ingest` now returns 4 outputs (profile, comp bar,
54
+ card, board); footer simplified (formula soup removed — belongs in README "How the numbers work").
55
+ - [done] Verify green: `py_compile` all *.py OK · comp_bar/card/board helpers render · board contains
56
+ rank1 + mb-rank-1/2/3 · run_ingest returns 4 values · narrate template fallback confirmed (no torch).
57
+
58
  ### Devin session — 2026-06-15
59
  - [done] GPU FIX — applied `@GPU` decorator to `narrate()` in narrate.py (line 60).
60
  ZeroGPU needs the inference function decorated or GPU alloc never happens and the
app.py CHANGED
@@ -5,6 +5,7 @@ Board ranks by Net Volumetric Yield (Υ). Four raw integers drive everything.
5
  """
6
  import gradio as gr
7
  import math as _math
 
8
  from metrics import compute, SEED
9
  from ingest import ingest_meta
10
  from theme import CSS
@@ -57,9 +58,10 @@ def board_html(extra=None):
57
  orders=_math.log10(ymax/y) if y>0 else 99
58
  barpct=max(2,100*(1-orders/5))
59
  d=f"{m['dev10x']:.2f}" if m['dev10x'] is not None else "\u2014"
60
- cls="mb-row you" if you else "mb-row"
 
61
  out.append(f'<div class="{cls}">'
62
- f'<span class="mb-rank">{i}</span>'
63
  f'<span class="mb-op"><b>{n}</b><br><span class="mb-raw">R {_fmt_int(m["raw"]["cache_read"])} \u00b7 C {_fmt_int(m["raw"]["cache_create"])} \u00b7 I {_fmt_int(m["raw"]["input"])} \u00b7 O {_fmt_int(m["raw"]["output"])}</span></span>'
64
  f'<span class="mb-num">{m["snr"]:.3f}</span>'
65
  f'<span class="mb-num">{d}</span>'
@@ -82,11 +84,61 @@ def classify(m):
82
  if v>=0.8 and l<2: return "Volatile Ingestor \u00b7 generates, doesn't retain"
83
  return "Transient \u00b7 low on both axes"
84
 
85
- def profile_md(name, m, rank, total_ops):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  c=m["composition"]; r=m["raw"]
87
  d=f"{m['dev10x']:.3f}" if m['dev10x'] is not None else "\u2014 non-compounding (no cache_create)"
88
- klass = classify(m)
89
- read = narrate(name, m, klass)
90
  cav = m.get("_caveat")
91
  cav_line = f"\n\n`\u26a0 {cav}`" if cav else ""
92
  cost_note = " (list-price estimate)" if m.get("cost_estimated") else " (from ccusage)"
@@ -104,8 +156,6 @@ ranked **#{rank}** of {total_ops} by \u03a5{cav_line}
104
  | cache_read | {r['cache_read']:,} |
105
  | **total** | **{m['total']:,}** |
106
 
107
- composition — read {c['read']:.2f}% \u00b7 create {c['create']:.2f}% \u00b7 output {c['output']:.2f}% \u00b7 input {c['input']:.3f}%
108
-
109
  ### board metrics
110
  | metric | value | |
111
  |---|---|---|
@@ -131,9 +181,9 @@ def run_ingest(blob, name):
131
  return ("Paste your `npx ccusage@latest --json` output, your "
132
  "`ccusage codex --json` output, or four numbers: "
133
  "input output cache_create cache_read.\n\n"
134
- f"_parser said: {e}_"), board_html()
135
  if i+o+cw+cr==0:
136
- return "Got zeros — check your paste.", board_html()
137
  m=compute(i,o,cw,cr, cost_usd=meta.get("cost"))
138
  if meta.get("estimated"):
139
  m["_caveat"]=meta.get("caveat")
@@ -148,7 +198,11 @@ def run_ingest(blob, name):
148
  rows=[(nn,compute(*vv)) for nn,vv in base.items() if nn!=name]+[(name,m)]
149
  rows.sort(key=lambda r:r[1]['yield'],reverse=True)
150
  rank=next(idx for idx,(nn,_) in enumerate(rows,1) if nn==name)
151
- return profile_md(name,m,rank,len(rows)), board_html((name,m))
 
 
 
 
152
 
153
  # ---------- UI ----------
154
  _blocks_kw = {"title": "MO\u00a7ES SigRank"}
@@ -160,6 +214,11 @@ with _b as demo:
160
  with gr.Column(elem_id="moses-hero"):
161
  gr.HTML("<h1>MO§ES\u2122 SigRank</h1>"
162
  "<p>the diagnostic x-ray of the token economy \u00b7 ranked by \u03a5 (Net Volumetric Yield) \u00b7 volume can't buy rank</p>")
 
 
 
 
 
163
 
164
  with gr.Tab("Leaderboard"):
165
  gr.Markdown("Ranked by **Υ = (Cache·Output)/Input²**. Raw Read·Create·In·Out stacked under each operator. $/1M is blended cost — efficient architecture is also the cheapest.")
@@ -182,10 +241,14 @@ with _b as demo:
182
  placeholder='{"totals":{"inputTokens":...}} or 1251211 11296121 128196310 2555179769')
183
  go = gr.Button("Compute my SigRank", variant="primary", elem_id="compute-btn")
184
  prof = gr.Markdown(elem_id="moses-profile")
 
 
 
 
185
  gr.Markdown("### your placement")
186
  gr.Markdown("*Live placement against the curated field — your row is transient (not saved to the board).*", elem_id="moses-foot")
187
  ob = gr.HTML(board_html())
188
- go.click(run_ingest, [blob, nm], [prof, ob])
189
  gr.Examples(
190
  examples=[
191
  ['{"totals":{"inputTokens":1251211,"outputTokens":11296121,"cacheCreationTokens":128196310,"cacheReadTokens":2555179769}}','MO§ES'],
@@ -193,10 +256,9 @@ with _b as demo:
193
  ],
194
  inputs=[blob, nm])
195
 
196
- gr.Markdown(elem_id="moses-foot", value="""**Four integers in, full ledger out.** input · output · cache_create · cache_read.
197
- SNR=O/(I+O) · 10x DEV=log₁₀(cascade) · Velocity=O/I · Leverage=C/I ·
198
- Efficiency=(C+O)/I÷4 · Avg $/1M=blended cost · Υ=(C·O)/I². Wild-corpus values
199
- provisional; MO§ES row from verified ccusage data. Codex rows estimated via 2:1 anchor.""")
200
 
201
  if __name__ == "__main__":
202
  try:
 
5
  """
6
  import gradio as gr
7
  import math as _math
8
+ import re as _re
9
  from metrics import compute, SEED
10
  from ingest import ingest_meta
11
  from theme import CSS
 
58
  orders=_math.log10(ymax/y) if y>0 else 99
59
  barpct=max(2,100*(1-orders/5))
60
  d=f"{m['dev10x']:.2f}" if m['dev10x'] is not None else "\u2014"
61
+ rank_cls = f"mb-rank-{i}" if i <= 3 else ""
62
+ cls = "mb-row you" if you else ("mb-row rank1" if i==1 else "mb-row")
63
  out.append(f'<div class="{cls}">'
64
+ f'<span class="mb-rank {rank_cls}">{i}</span>'
65
  f'<span class="mb-op"><b>{n}</b><br><span class="mb-raw">R {_fmt_int(m["raw"]["cache_read"])} \u00b7 C {_fmt_int(m["raw"]["cache_create"])} \u00b7 I {_fmt_int(m["raw"]["input"])} \u00b7 O {_fmt_int(m["raw"]["output"])}</span></span>'
66
  f'<span class="mb-num">{m["snr"]:.3f}</span>'
67
  f'<span class="mb-num">{d}</span>'
 
84
  if v>=0.8 and l<2: return "Volatile Ingestor \u00b7 generates, doesn't retain"
85
  return "Transient \u00b7 low on both axes"
86
 
87
+ def comp_bar_html(c):
88
+ return (f'<div class="comp-bar">'
89
+ f'<div class="comp-read" style="width:{c["read"]:.1f}%"></div>'
90
+ f'<div class="comp-create" style="width:{c["create"]:.1f}%"></div>'
91
+ f'<div class="comp-output" style="width:{c["output"]:.1f}%"></div>'
92
+ f'<div class="comp-input" style="width:{c["input"]:.3f}%"></div>'
93
+ f'</div>'
94
+ f'<div style="font-size:10px;color:#8a7f68;margin-bottom:8px">'
95
+ f'read {c["read"]:.1f}% \u00b7 create {c["create"]:.1f}% \u00b7 output {c["output"]:.1f}% \u00b7 input {c["input"]:.3f}%'
96
+ f'</div>')
97
+
98
+ def _first_sentence(text, limit=120):
99
+ t = _re.sub(r"[*_`>#]", "", text or "").replace("\n", " ").strip()
100
+ parts = _re.split(r"(?<=[.!?])\s", t, maxsplit=1)
101
+ s = parts[0] if parts else t
102
+ if len(s) > limit:
103
+ s = s[:limit].rstrip() + "\u2026"
104
+ return s
105
+
106
+ def card_html(name, m, rank, total_ops, narration_text):
107
+ archetype = classify(m).split("\u00b7")[0].strip()
108
+ c = m["composition"]
109
+ if m["transmission"] is not None:
110
+ cascade = (
111
+ f'<div class="sig-card-cascade-box">{m["transmission"]:.1f}\u00d7<small>trans</small></div>'
112
+ f'<span class="sig-card-cascade-arrow">\u2192</span>'
113
+ f'<div class="sig-card-cascade-box">{m["commitment"]:.1f}\u00d7<small>commit</small></div>'
114
+ f'<span class="sig-card-cascade-arrow">\u2192</span>'
115
+ f'<div class="sig-card-cascade-box">{m["reuse"]:.1f}\u00d7<small>reuse</small></div>'
116
+ f'<span class="sig-card-cascade-arrow">=</span>'
117
+ f'<div class="sig-card-cascade-box">{m["leverage"]:,.0f}\u00d7<small>leverage</small></div>'
118
+ )
119
+ else:
120
+ cascade = '<div class="sig-card-cascade-box">\u2014<small>non-compounding</small></div>'
121
+ quote = _first_sentence(narration_text)
122
+ return (
123
+ '<div class="sig-card">'
124
+ '<div class="sig-card-watermark">MO\u00a7ES\u2122 SIGRANK</div>'
125
+ f'<div class="sig-card-name">{name}</div>'
126
+ f'<div class="sig-card-archetype">{archetype}</div>'
127
+ f'<div class="sig-card-yield">{m["yield"]:,.0f}</div>'
128
+ '<div class="sig-card-yield-label">net volumetric yield</div>'
129
+ f'<div class="sig-card-rank">#<span>{rank}</span> of {total_ops} operators</div>'
130
+ f'<div class="sig-card-cascade">{cascade}</div>'
131
+ f'{comp_bar_html(c)}'
132
+ f'<div class="sig-card-quote">{quote}</div>'
133
+ '<div class="sig-card-footer"><span>sigrank.hf.space</span><span>\u03a5=(C\u00b7O)/I\u00b2</span></div>'
134
+ '</div>'
135
+ )
136
+
137
+ def profile_md(name, m, rank, total_ops, read=None):
138
  c=m["composition"]; r=m["raw"]
139
  d=f"{m['dev10x']:.3f}" if m['dev10x'] is not None else "\u2014 non-compounding (no cache_create)"
140
+ if read is None:
141
+ read = narrate(name, m, classify(m))
142
  cav = m.get("_caveat")
143
  cav_line = f"\n\n`\u26a0 {cav}`" if cav else ""
144
  cost_note = " (list-price estimate)" if m.get("cost_estimated") else " (from ccusage)"
 
156
  | cache_read | {r['cache_read']:,} |
157
  | **total** | **{m['total']:,}** |
158
 
 
 
159
  ### board metrics
160
  | metric | value | |
161
  |---|---|---|
 
181
  return ("Paste your `npx ccusage@latest --json` output, your "
182
  "`ccusage codex --json` output, or four numbers: "
183
  "input output cache_create cache_read.\n\n"
184
+ f"_parser said: {e}_"), "", "", board_html()
185
  if i+o+cw+cr==0:
186
+ return "Got zeros — check your paste.", "", "", board_html()
187
  m=compute(i,o,cw,cr, cost_usd=meta.get("cost"))
188
  if meta.get("estimated"):
189
  m["_caveat"]=meta.get("caveat")
 
198
  rows=[(nn,compute(*vv)) for nn,vv in base.items() if nn!=name]+[(name,m)]
199
  rows.sort(key=lambda r:r[1]['yield'],reverse=True)
200
  rank=next(idx for idx,(nn,_) in enumerate(rows,1) if nn==name)
201
+ read = narrate(name, m, classify(m))
202
+ return (profile_md(name,m,rank,len(rows),read),
203
+ comp_bar_html(m["composition"]),
204
+ card_html(name,m,rank,len(rows),read),
205
+ board_html((name,m)))
206
 
207
  # ---------- UI ----------
208
  _blocks_kw = {"title": "MO\u00a7ES SigRank"}
 
214
  with gr.Column(elem_id="moses-hero"):
215
  gr.HTML("<h1>MO§ES\u2122 SigRank</h1>"
216
  "<p>the diagnostic x-ray of the token economy \u00b7 ranked by \u03a5 (Net Volumetric Yield) \u00b7 volume can't buy rank</p>")
217
+ gr.HTML('<div id="moses-stat-strip">'
218
+ '<div>operators ranked <span>7</span></div>'
219
+ '<div>MO§ES leads by <span>3,141\u00d7</span></div>'
220
+ '<div>architecture beats budget</div>'
221
+ '</div>')
222
 
223
  with gr.Tab("Leaderboard"):
224
  gr.Markdown("Ranked by **Υ = (Cache·Output)/Input²**. Raw Read·Create·In·Out stacked under each operator. $/1M is blended cost — efficient architecture is also the cheapest.")
 
241
  placeholder='{"totals":{"inputTokens":...}} or 1251211 11296121 128196310 2555179769')
242
  go = gr.Button("Compute my SigRank", variant="primary", elem_id="compute-btn")
243
  prof = gr.Markdown(elem_id="moses-profile")
244
+ prof_bar = gr.HTML()
245
+ gr.Markdown("### share card")
246
+ card = gr.HTML()
247
+ gr.Markdown("*Screenshot to share \u00b7 right-click \u2192 Save image*", elem_id="moses-foot")
248
  gr.Markdown("### your placement")
249
  gr.Markdown("*Live placement against the curated field — your row is transient (not saved to the board).*", elem_id="moses-foot")
250
  ob = gr.HTML(board_html())
251
+ go.click(run_ingest, [blob, nm], [prof, prof_bar, card, ob])
252
  gr.Examples(
253
  examples=[
254
  ['{"totals":{"inputTokens":1251211,"outputTokens":11296121,"cacheCreationTokens":128196310,"cacheReadTokens":2555179769}}','MO§ES'],
 
256
  ],
257
  inputs=[blob, nm])
258
 
259
+ gr.Markdown(elem_id="moses-foot", value="""Four integers in, full ledger out.
260
+ Architecture is the only variable that matters.
261
+ Wild-corpus values provisional · MO§ES row verified ccusage · [How it works ↗](#)""")
 
262
 
263
  if __name__ == "__main__":
264
  try:
theme.py CHANGED
@@ -13,10 +13,26 @@ CSS = """
13
  .gradio-container { background: var(--moses-bg) !important; max-width: 1100px !important; margin: 0 auto !important; }
14
  .dark .gradio-container, body { background: var(--moses-bg) !important; }
15
 
16
- #moses-hero { border-bottom: 1px solid var(--moses-gold); padding: 8px 0 16px; margin-bottom: 8px; }
17
- #moses-hero h1 { color: var(--moses-gold) !important; font-size: 26px !important; letter-spacing: 0.14em !important;
18
- font-weight: 700 !important; margin: 0 !important; }
19
- #moses-hero p { color: var(--moses-dim) !important; font-size: 13px !important; margin: 4px 0 0 !important; letter-spacing: 0.04em; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  .tabs, .tabitem { background: transparent !important; border: none !important; }
22
  button.selected { color: var(--moses-gold) !important; border-bottom: 2px solid var(--moses-gold) !important; }
@@ -62,15 +78,89 @@ button.primary:hover, #compute-btn:hover { background: #d8a449 !important; }
62
  .mb-row { border-bottom: 1px solid #3A3324; color: #8a7f68; font-size: 12px; }
63
  .mb-row.you { background: rgba(196,146,58,0.10); color: #E8E0CF; }
64
  .mb-row.you b { color: #C4923A; }
 
 
 
 
 
 
 
 
 
65
  .mb-row b { color: #E8E0CF; font-family: var(--font-sans, sans-serif); font-size: 13px; }
66
- .mb-raw { color: #5f573f; font-size: 9.5px; }
67
  .mb-num { text-align: right; }
68
  .mb-y { position: relative; display: flex; align-items: center; justify-content: flex-end; gap: 6px; min-height: 20px; }
69
  .mb-bar { position: absolute; left: 0; top: 50%; transform: translateY(-50%); height: 14px;
70
- background: linear-gradient(90deg, rgba(196,146,58,0.15), rgba(196,146,58,0.55)); border-radius: 2px; }
71
  .mb-row.you .mb-bar { background: linear-gradient(90deg, rgba(196,146,58,0.4), #C4923A); }
72
  .mb-yval { position: relative; z-index: 2; color: #E8E0CF; font-weight: 700; font-size: 12px; padding-right: 4px; }
73
  .mb-foot { color: #5f573f; font-size: 10.5px; padding: 10px 8px 0; line-height: 1.6; }
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  footer { display: none !important; }
 
 
 
 
 
 
 
 
 
 
76
  """
 
13
  .gradio-container { background: var(--moses-bg) !important; max-width: 1100px !important; margin: 0 auto !important; }
14
  .dark .gradio-container, body { background: var(--moses-bg) !important; }
15
 
16
+ #moses-hero {
17
+ border-bottom: 1px solid var(--moses-gold);
18
+ padding: 16px 0 20px;
19
+ margin-bottom: 12px;
20
+ position: relative;
21
+ }
22
+ #moses-hero h1 {
23
+ color: var(--moses-gold) !important;
24
+ font-size: 32px !important;
25
+ letter-spacing: 0.16em !important;
26
+ font-weight: 700 !important;
27
+ margin: 0 !important;
28
+ }
29
+ #moses-hero p { color: var(--moses-dim) !important; font-size: 12px !important; margin: 6px 0 0 !important; }
30
+ /* stat strip */
31
+ #moses-stat-strip {
32
+ display: flex; gap: 24px; margin-top: 10px;
33
+ font-size: 11px; color: var(--moses-dim); letter-spacing: 0.04em;
34
+ }
35
+ #moses-stat-strip span { color: var(--moses-ink); font-weight: 700; }
36
 
37
  .tabs, .tabitem { background: transparent !important; border: none !important; }
38
  button.selected { color: var(--moses-gold) !important; border-bottom: 2px solid var(--moses-gold) !important; }
 
78
  .mb-row { border-bottom: 1px solid #3A3324; color: #8a7f68; font-size: 12px; }
79
  .mb-row.you { background: rgba(196,146,58,0.10); color: #E8E0CF; }
80
  .mb-row.you b { color: #C4923A; }
81
+ .mb-row.rank1 {
82
+ border-left: 3px solid var(--moses-gold);
83
+ background: rgba(196,146,58,0.12) !important;
84
+ color: var(--moses-ink) !important;
85
+ }
86
+ .mb-row.rank1 .mb-yval { color: var(--moses-gold); font-size: 14px; }
87
+ .mb-rank-1 { color: var(--moses-gold); font-weight: 700; }
88
+ .mb-rank-2 { color: #a0a0a0; }
89
+ .mb-rank-3 { color: #8a6a3a; }
90
  .mb-row b { color: #E8E0CF; font-family: var(--font-sans, sans-serif); font-size: 13px; }
91
+ .mb-raw { color: #7a7060; font-size: 9.5px; }
92
  .mb-num { text-align: right; }
93
  .mb-y { position: relative; display: flex; align-items: center; justify-content: flex-end; gap: 6px; min-height: 20px; }
94
  .mb-bar { position: absolute; left: 0; top: 50%; transform: translateY(-50%); height: 14px;
95
+ background: linear-gradient(90deg, rgba(196,146,58,0.28), rgba(196,146,58,0.72)); border-radius: 2px; }
96
  .mb-row.you .mb-bar { background: linear-gradient(90deg, rgba(196,146,58,0.4), #C4923A); }
97
  .mb-yval { position: relative; z-index: 2; color: #E8E0CF; font-weight: 700; font-size: 12px; padding-right: 4px; }
98
  .mb-foot { color: #5f573f; font-size: 10.5px; padding: 10px 8px 0; line-height: 1.6; }
99
 
100
+ /* composition bar */
101
+ .comp-bar { display: flex; height: 6px; border-radius: 3px; overflow: hidden; margin: 6px 0 10px; width: 100%; }
102
+ .comp-read { background: var(--moses-gold); }
103
+ .comp-create { background: #8a6a3a; }
104
+ .comp-output { background: #5a7a5a; }
105
+ .comp-input { background: #3a4a5a; }
106
+
107
+ /* trading card */
108
+ .sig-card {
109
+ width: 380px; min-height: 500px;
110
+ background: linear-gradient(160deg, #1E1B15 60%, #2a2318);
111
+ border: 1px solid var(--moses-gold);
112
+ border-radius: 12px;
113
+ padding: 28px 24px 20px;
114
+ font-family: ui-monospace, monospace;
115
+ position: relative;
116
+ box-shadow: 0 0 40px rgba(196,146,58,0.12), inset 0 1px 0 rgba(196,146,58,0.2);
117
+ }
118
+ .sig-card-watermark {
119
+ position: absolute; top: 14px; right: 16px;
120
+ font-size: 9px; color: var(--moses-dim); letter-spacing: 0.1em;
121
+ }
122
+ .sig-card-name { color: var(--moses-gold); font-size: 22px; font-weight: 700; letter-spacing: 0.1em; margin-bottom: 4px; }
123
+ .sig-card-archetype {
124
+ display: inline-block; font-size: 10px; letter-spacing: 0.08em;
125
+ border: 1px solid var(--moses-gold); color: var(--moses-gold);
126
+ padding: 2px 8px; border-radius: 2px; margin-bottom: 20px; text-transform: uppercase;
127
+ }
128
+ .sig-card-yield { font-size: 52px; font-weight: 700; color: var(--moses-ink); line-height: 1; }
129
+ .sig-card-yield-label { font-size: 10px; color: var(--moses-dim); letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 20px; }
130
+ .sig-card-rank { font-size: 13px; color: var(--moses-dim); margin-bottom: 16px; }
131
+ .sig-card-rank span { color: var(--moses-ink); font-weight: 700; }
132
+ .sig-card-cascade {
133
+ display: flex; align-items: center; gap: 6px;
134
+ font-size: 11px; margin-bottom: 16px;
135
+ }
136
+ .sig-card-cascade-box {
137
+ border: 1px solid #3A3324; padding: 4px 8px; border-radius: 3px;
138
+ color: var(--moses-ink); text-align: center; min-width: 52px;
139
+ }
140
+ .sig-card-cascade-box small { display: block; color: var(--moses-dim); font-size: 9px; }
141
+ .sig-card-cascade-arrow { color: var(--moses-dim); }
142
+ .sig-card-quote {
143
+ font-size: 11px; color: var(--moses-dim);
144
+ border-left: 2px solid var(--moses-gold);
145
+ padding: 8px 12px; margin-top: 16px; line-height: 1.5;
146
+ font-style: italic;
147
+ }
148
+ .sig-card-footer {
149
+ position: absolute; bottom: 16px; left: 24px; right: 24px;
150
+ font-size: 9px; color: #5f573f; letter-spacing: 0.06em;
151
+ border-top: 1px solid #3A3324; padding-top: 8px;
152
+ display: flex; justify-content: space-between;
153
+ }
154
+
155
  footer { display: none !important; }
156
+
157
+ @media (max-width: 700px) {
158
+ .mb-head, .mb-row {
159
+ grid-template-columns: 22px 1fr 0.5fr 0.5fr 1fr;
160
+ font-size: 11px;
161
+ }
162
+ /* hide velocity and leverage columns on mobile */
163
+ .mb-head span:nth-child(5), .mb-head span:nth-child(6),
164
+ .mb-row span:nth-child(5), .mb-row span:nth-child(6) { display: none; }
165
+ }
166
  """