Commit ·
38091a2
1
Parent(s): 1441c1f
Add /tools suite page cross-linking Macro Intelligence
Browse filesNew data-driven /tools page presenting the maker's tools as one suite:
Sentiments Analyzer (this) + Macro Intelligence (macro.areebalikhan.com),
so users discover the other tools. Card grid with tags, "You are here"
badge, ItemList structured data. Adds "Tools" to the site navbar, landing
nav, footer and the terminal topbar; robots.txt + sitemap include it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- content/site_content.py +99 -0
- server.py +8 -0
- templates/index.html +1 -0
- templates/join.html +1 -0
content/site_content.py
CHANGED
|
@@ -55,6 +55,37 @@ AUTHOR = {
|
|
| 55 |
"site": "https://areebalikhan.com/",
|
| 56 |
}
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
# ---------------------------------------------------------------------------
|
| 59 |
# ARTICLES (long-tail, educational, each internally links back to the product)
|
| 60 |
# body is a list of ("h2", text) / ("p", html) / ("ul", [items]) / ("cta", None)
|
|
@@ -493,6 +524,7 @@ def _nav(path: str) -> str:
|
|
| 493 |
<span class="n">SENTIMENTS <b>ANALYZER</b></span>
|
| 494 |
</a>
|
| 495 |
<span class="navcta">
|
|
|
|
| 496 |
<a class="link"{cls('/articles')} href="/articles">Articles</a>
|
| 497 |
<a class="link"{cls('/faq')} href="/faq">FAQ</a>
|
| 498 |
<a class="link"{cls('/about')} href="/about">About</a>
|
|
@@ -508,6 +540,7 @@ def _footer() -> str:
|
|
| 508 |
return f"""<footer>
|
| 509 |
© {year} {SITE_NAME}
|
| 510 |
· <a href="/">Home</a>
|
|
|
|
| 511 |
· <a href="/articles">Articles</a>
|
| 512 |
· <a href="/faq">FAQ</a>
|
| 513 |
· <a href="/about">About</a>
|
|
@@ -561,6 +594,26 @@ nav .btn:hover{background:var(--accent2)}
|
|
| 561 |
.page-head h1{font-size:32px;font-weight:800;letter-spacing:-0.5px}
|
| 562 |
.page-head h1 .g{color:var(--accent)}
|
| 563 |
.page-head p{color:var(--dim);font-size:15px;margin-top:12px;max-width:600px;margin-left:auto;margin-right:auto}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
.eyebrow{color:var(--accent);font-size:12px;letter-spacing:1.6px;font-weight:800;text-transform:uppercase}
|
| 565 |
/* article list cards */
|
| 566 |
.alist{display:grid;gap:14px;margin-top:30px}
|
|
@@ -928,5 +981,51 @@ def render_about(base: str) -> str:
|
|
| 928 |
body, head_extra=_jsonld(ld))
|
| 929 |
|
| 930 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 931 |
def article_slugs() -> list[str]:
|
| 932 |
return [a["slug"] for a in ARTICLES]
|
|
|
|
| 55 |
"site": "https://areebalikhan.com/",
|
| 56 |
}
|
| 57 |
|
| 58 |
+
# ---------------------------------------------------------------------------
|
| 59 |
+
# TOOLS (the maker's suite — cross-links related products for discovery + SEO)
|
| 60 |
+
# Set "current": True on the tool that IS this site.
|
| 61 |
+
# ---------------------------------------------------------------------------
|
| 62 |
+
TOOLS = [
|
| 63 |
+
{
|
| 64 |
+
"name": "Sentiments Analyzer",
|
| 65 |
+
"kind": "Crypto sentiment & narratives",
|
| 66 |
+
"tagline": "Read the crypto market's mood in one number.",
|
| 67 |
+
"description": "Reads hundreds of crypto headlines and posts a day, scores "
|
| 68 |
+
"sentiment 0-100, tracks trending narratives, flags manufactured "
|
| 69 |
+
"hype and shows sentiment vs price, with an AI analyst and a "
|
| 70 |
+
"transparent, publicly-scored track record.",
|
| 71 |
+
"tags": ["Sentiment", "Narratives", "AI analyst", "Public API"],
|
| 72 |
+
"url": "/terminal",
|
| 73 |
+
"current": True,
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"name": "Macro Intelligence",
|
| 77 |
+
"kind": "Global macro regime & risk",
|
| 78 |
+
"tagline": "Live global macro regime & risk score.",
|
| 79 |
+
"description": "Synthesizes the dollar, interest rates, inflation, growth and "
|
| 80 |
+
"central-bank policy into one explainable 0-100 macro score, with "
|
| 81 |
+
"live regime classification, indicator z-scores, signal "
|
| 82 |
+
"contributions and email alerts on regime shifts.",
|
| 83 |
+
"tags": ["Macro", "Risk score", "Regime", "Alerts"],
|
| 84 |
+
"url": "https://macro.areebalikhan.com/",
|
| 85 |
+
"current": False,
|
| 86 |
+
},
|
| 87 |
+
]
|
| 88 |
+
|
| 89 |
# ---------------------------------------------------------------------------
|
| 90 |
# ARTICLES (long-tail, educational, each internally links back to the product)
|
| 91 |
# body is a list of ("h2", text) / ("p", html) / ("ul", [items]) / ("cta", None)
|
|
|
|
| 524 |
<span class="n">SENTIMENTS <b>ANALYZER</b></span>
|
| 525 |
</a>
|
| 526 |
<span class="navcta">
|
| 527 |
+
<a class="link"{cls('/tools')} href="/tools">Tools</a>
|
| 528 |
<a class="link"{cls('/articles')} href="/articles">Articles</a>
|
| 529 |
<a class="link"{cls('/faq')} href="/faq">FAQ</a>
|
| 530 |
<a class="link"{cls('/about')} href="/about">About</a>
|
|
|
|
| 540 |
return f"""<footer>
|
| 541 |
© {year} {SITE_NAME}
|
| 542 |
· <a href="/">Home</a>
|
| 543 |
+
· <a href="/tools">Tools</a>
|
| 544 |
· <a href="/articles">Articles</a>
|
| 545 |
· <a href="/faq">FAQ</a>
|
| 546 |
· <a href="/about">About</a>
|
|
|
|
| 594 |
.page-head h1{font-size:32px;font-weight:800;letter-spacing:-0.5px}
|
| 595 |
.page-head h1 .g{color:var(--accent)}
|
| 596 |
.page-head p{color:var(--dim);font-size:15px;margin-top:12px;max-width:600px;margin-left:auto;margin-right:auto}
|
| 597 |
+
/* tools suite */
|
| 598 |
+
.tools{display:grid;gap:16px;margin-top:30px}
|
| 599 |
+
.tool{display:block;background:var(--panel);backdrop-filter:blur(10px);border:1px solid var(--border);
|
| 600 |
+
border-radius:16px;padding:24px;transition:.15s;position:relative}
|
| 601 |
+
.tool:hover{border-color:rgba(232,163,61,0.45);transform:translateY(-2px)}
|
| 602 |
+
.tool .kind{color:var(--accent);font-size:11px;letter-spacing:1.4px;font-weight:800;text-transform:uppercase}
|
| 603 |
+
.tool h2{color:var(--white);font-size:21px;font-weight:800;margin:6px 0 4px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
| 604 |
+
.tool .badge{font-size:10px;font-weight:700;letter-spacing:.5px;padding:2px 9px;border-radius:20px;
|
| 605 |
+
border:1px solid var(--border);color:var(--dim)}
|
| 606 |
+
.tool .badge.here{color:var(--accent);border-color:rgba(232,163,61,0.4);background:rgba(232,163,61,0.08)}
|
| 607 |
+
.tool .badge.live{color:var(--green);border-color:rgba(43,214,123,0.4);background:rgba(43,214,123,0.08)}
|
| 608 |
+
.tool .tl2{color:#c8ccd6;font-size:14.5px;font-weight:600;margin-bottom:8px}
|
| 609 |
+
.tool .desc{color:var(--dim);font-size:14px;line-height:1.7}
|
| 610 |
+
.tool .tags{display:flex;gap:7px;flex-wrap:wrap;margin-top:14px}
|
| 611 |
+
.tool .tags span{font-size:11.5px;color:var(--dim);background:rgba(255,255,255,0.04);
|
| 612 |
+
border:1px solid var(--border);border-radius:7px;padding:4px 10px;font-family:var(--mono)}
|
| 613 |
+
.tool .go{display:inline-block;margin-top:16px;color:var(--accent);font-weight:700;font-size:13.5px}
|
| 614 |
+
.tool .go:hover{color:var(--accent2)}
|
| 615 |
+
:root[data-theme="light"] .tool .tl2{color:#333a45}
|
| 616 |
+
:root[data-theme="light"] .tool .tags span{background:rgba(0,0,0,0.03)}
|
| 617 |
.eyebrow{color:var(--accent);font-size:12px;letter-spacing:1.6px;font-weight:800;text-transform:uppercase}
|
| 618 |
/* article list cards */
|
| 619 |
.alist{display:grid;gap:14px;margin-top:30px}
|
|
|
|
| 981 |
body, head_extra=_jsonld(ld))
|
| 982 |
|
| 983 |
|
| 984 |
+
def render_tools(base: str) -> str:
|
| 985 |
+
import json
|
| 986 |
+
cards = []
|
| 987 |
+
for t in TOOLS:
|
| 988 |
+
ext = t["url"].startswith("http")
|
| 989 |
+
attrs = ' target="_blank" rel="noopener"' if ext else ""
|
| 990 |
+
badge = ('<span class="badge here">You are here</span>' if t.get("current")
|
| 991 |
+
else '<span class="badge live">Live</span>')
|
| 992 |
+
tags = "".join(f"<span>{escape(x)}</span>" for x in t["tags"])
|
| 993 |
+
go = ("Explore this tool →" if not t.get("current")
|
| 994 |
+
else "Open the terminal →")
|
| 995 |
+
go_href = "/terminal" if t.get("current") else t["url"]
|
| 996 |
+
cards.append(
|
| 997 |
+
f'<a class="tool" href="{escape(t["url"])}"{attrs}>'
|
| 998 |
+
f'<div class="kind">{escape(t["kind"])}</div>'
|
| 999 |
+
f'<h2>{escape(t["name"])} {badge}</h2>'
|
| 1000 |
+
f'<div class="tl2">{escape(t["tagline"])}</div>'
|
| 1001 |
+
f'<div class="desc">{escape(t["description"])}</div>'
|
| 1002 |
+
f'<div class="tags">{tags}</div>'
|
| 1003 |
+
f'<span class="go">{go}</span></a>'
|
| 1004 |
+
)
|
| 1005 |
+
body = (
|
| 1006 |
+
'<div class="page-head">'
|
| 1007 |
+
'<span class="eyebrow">The toolkit</span>'
|
| 1008 |
+
'<h1>More <span class="g">market intelligence</span> tools</h1>'
|
| 1009 |
+
'<p>A small suite of tools built by the same maker, each turns a complex '
|
| 1010 |
+
'market signal into one clear, explainable number. Explore the others below.</p>'
|
| 1011 |
+
'</div>'
|
| 1012 |
+
f'<div class="tools">{"".join(cards)}</div>'
|
| 1013 |
+
)
|
| 1014 |
+
ld = json.dumps({
|
| 1015 |
+
"@context": "https://schema.org",
|
| 1016 |
+
"@type": "ItemList",
|
| 1017 |
+
"itemListElement": [
|
| 1018 |
+
{"@type": "ListItem", "position": i + 1, "name": t["name"],
|
| 1019 |
+
"url": (t["url"] if t["url"].startswith("http") else f"{base}{t['url']}")}
|
| 1020 |
+
for i, t in enumerate(TOOLS)
|
| 1021 |
+
],
|
| 1022 |
+
})
|
| 1023 |
+
return _shell(base, "/tools", f"Tools · {SITE_NAME}",
|
| 1024 |
+
"A suite of market-intelligence tools by Areeb Ali: Sentiments "
|
| 1025 |
+
"Analyzer for crypto sentiment & narratives, and Macro Intelligence "
|
| 1026 |
+
"for a live global macro regime & risk score.",
|
| 1027 |
+
body, head_extra=_jsonld(ld))
|
| 1028 |
+
|
| 1029 |
+
|
| 1030 |
def article_slugs() -> list[str]:
|
| 1031 |
return [a["slug"] for a in ARTICLES]
|
server.py
CHANGED
|
@@ -734,6 +734,11 @@ async def about_page(request: Request):
|
|
| 734 |
return HTMLResponse(site_content.render_about(_public_base(request)))
|
| 735 |
|
| 736 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 737 |
# ---------------------------------------------------------------------------
|
| 738 |
# Auth pages + API (accounts foundation; terminal stays preview-gated for now)
|
| 739 |
# ---------------------------------------------------------------------------
|
|
@@ -1005,6 +1010,7 @@ async def robots(request: Request):
|
|
| 1005 |
body = (
|
| 1006 |
"User-agent: *\n"
|
| 1007 |
"Allow: /$\n"
|
|
|
|
| 1008 |
"Allow: /articles\n"
|
| 1009 |
"Allow: /faq\n"
|
| 1010 |
"Allow: /about\n"
|
|
@@ -1028,6 +1034,8 @@ async def sitemap(request: Request):
|
|
| 1028 |
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
|
| 1029 |
f" <url><loc>{base}/</loc><lastmod>{today}</lastmod>"
|
| 1030 |
"<changefreq>daily</changefreq><priority>1.0</priority></url>\n"
|
|
|
|
|
|
|
| 1031 |
f" <url><loc>{base}/articles</loc><lastmod>{today}</lastmod>"
|
| 1032 |
"<changefreq>weekly</changefreq><priority>0.8</priority></url>\n"
|
| 1033 |
f" <url><loc>{base}/faq</loc><lastmod>{today}</lastmod>"
|
|
|
|
| 734 |
return HTMLResponse(site_content.render_about(_public_base(request)))
|
| 735 |
|
| 736 |
|
| 737 |
+
@app.api_route("/tools", methods=["GET", "HEAD"], response_class=HTMLResponse)
|
| 738 |
+
async def tools_page(request: Request):
|
| 739 |
+
return HTMLResponse(site_content.render_tools(_public_base(request)))
|
| 740 |
+
|
| 741 |
+
|
| 742 |
# ---------------------------------------------------------------------------
|
| 743 |
# Auth pages + API (accounts foundation; terminal stays preview-gated for now)
|
| 744 |
# ---------------------------------------------------------------------------
|
|
|
|
| 1010 |
body = (
|
| 1011 |
"User-agent: *\n"
|
| 1012 |
"Allow: /$\n"
|
| 1013 |
+
"Allow: /tools\n"
|
| 1014 |
"Allow: /articles\n"
|
| 1015 |
"Allow: /faq\n"
|
| 1016 |
"Allow: /about\n"
|
|
|
|
| 1034 |
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
|
| 1035 |
f" <url><loc>{base}/</loc><lastmod>{today}</lastmod>"
|
| 1036 |
"<changefreq>daily</changefreq><priority>1.0</priority></url>\n"
|
| 1037 |
+
f" <url><loc>{base}/tools</loc><lastmod>{today}</lastmod>"
|
| 1038 |
+
"<changefreq>monthly</changefreq><priority>0.7</priority></url>\n"
|
| 1039 |
f" <url><loc>{base}/articles</loc><lastmod>{today}</lastmod>"
|
| 1040 |
"<changefreq>weekly</changefreq><priority>0.8</priority></url>\n"
|
| 1041 |
f" <url><loc>{base}/faq</loc><lastmod>{today}</lastmod>"
|
templates/index.html
CHANGED
|
@@ -975,6 +975,7 @@ body.hint-dismissed #beginner-hint { display: none !important; }
|
|
| 975 |
</span>
|
| 976 |
<a class="tb-link" id="tb-brief" href="/signup" style="display:none;background:var(--accent);color:#0a0b0e;border-color:var(--accent);font-weight:700">📩 Get daily brief</a>
|
| 977 |
<a class="tb-link" id="tb-account" href="/account" style="display:none">👤 Account</a>
|
|
|
|
| 978 |
<a class="tb-link" href="https://areeb.instatus.com" target="_blank" title="Service status">● STATUS</a>
|
| 979 |
<a class="tb-link" href="/terms" target="_blank">TERMS</a>
|
| 980 |
<button class="theme-toggle" id="theme-toggle" onclick="toggleTheme()" title="Toggle light/dark" aria-label="Toggle theme">☽</button>
|
|
|
|
| 975 |
</span>
|
| 976 |
<a class="tb-link" id="tb-brief" href="/signup" style="display:none;background:var(--accent);color:#0a0b0e;border-color:var(--accent);font-weight:700">📩 Get daily brief</a>
|
| 977 |
<a class="tb-link" id="tb-account" href="/account" style="display:none">👤 Account</a>
|
| 978 |
+
<a class="tb-link" href="/tools" title="More tools">TOOLS</a>
|
| 979 |
<a class="tb-link" href="https://areeb.instatus.com" target="_blank" title="Service status">● STATUS</a>
|
| 980 |
<a class="tb-link" href="/terms" target="_blank">TERMS</a>
|
| 981 |
<button class="theme-toggle" id="theme-toggle" onclick="toggleTheme()" title="Toggle light/dark" aria-label="Toggle theme">☽</button>
|
templates/join.html
CHANGED
|
@@ -147,6 +147,7 @@ footer a{color:var(--dim)}
|
|
| 147 |
</a>
|
| 148 |
<span class="navcta">
|
| 149 |
<a class="link" href="#how">How it works</a>
|
|
|
|
| 150 |
<a class="link" href="/articles">Articles</a>
|
| 151 |
<a class="link" href="/faq">FAQ</a>
|
| 152 |
<a class="link" href="/about">About</a>
|
|
|
|
| 147 |
</a>
|
| 148 |
<span class="navcta">
|
| 149 |
<a class="link" href="#how">How it works</a>
|
| 150 |
+
<a class="link" href="/tools">Tools</a>
|
| 151 |
<a class="link" href="/articles">Articles</a>
|
| 152 |
<a class="link" href="/faq">FAQ</a>
|
| 153 |
<a class="link" href="/about">About</a>
|