Skip to content

Commit 29161c6

Browse files
Meyanis95claude
andcommitted
fix: address PR #31 review feedback
- add favicon (indigo brand mark) wired into both layouts - strip inline markdown from extracted summaries so vendor descriptions render as plain text - migrate landing to Guide layout so the top nav matches sub-pages - hide "What institutions tell us" testimonials section pending real social proof - spell out CROPS / I2I / I2U inline on the eval section Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent fcdc5ee commit 29161c6

5 files changed

Lines changed: 46 additions & 182 deletions

File tree

public/favicon.svg

Lines changed: 9 additions & 0 deletions
Loading

scripts/build-graph.mjs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,40 @@ export function fileToNodeId(dirType, filename, prefix) {
9999
return `${dirType}/${fileToSlug(filename, prefix)}`;
100100
}
101101

102-
/** First paragraph from ## Intent (patterns) or first paragraph after ## section (others). Verbatim. */
102+
/** Strip inline markdown formatting so summary fields render as plain text
103+
* wherever they're displayed (vendor list, OG description, etc.). Body
104+
* content elsewhere keeps its markdown — only summaries are flattened. */
105+
function stripInlineMarkdown(text) {
106+
return text
107+
.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1") // images → alt text
108+
.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1") // links → label
109+
.replace(/`([^`]+)`/g, "$1") // code spans
110+
.replace(/\*\*([^*]+)\*\*/g, "$1") // bold
111+
.replace(/__([^_]+)__/g, "$1") // bold (alt)
112+
.replace(/\*([^*]+)\*/g, "$1") // italic
113+
.replace(/_([^_]+)_/g, "$1") // italic (alt)
114+
.replace(/~~([^~]+)~~/g, "$1"); // strikethrough
115+
}
116+
117+
/** First paragraph from ## Intent (patterns) or first paragraph after ## section (others). */
103118
export function extractSummary(body, type) {
104119
const intentMatch = body.match(/## Intent\s*\n+([\s\S]*?)(?=\n## |\n$)/);
105-
if (intentMatch) return firstParagraph(intentMatch[1]);
120+
if (intentMatch) return stripInlineMarkdown(firstParagraph(intentMatch[1]));
106121

107122
const tldrMatch = body.match(/## TLDR\s*\n+([\s\S]*?)(?=\n## |\n$)/);
108-
if (tldrMatch) return firstParagraph(tldrMatch[1]).replace(/^-\s*/, "");
123+
if (tldrMatch)
124+
return stripInlineMarkdown(firstParagraph(tldrMatch[1]).replace(/^-\s*/, ""));
109125

110126
const whatMatch = body.match(/## What it is\s*\n+([\s\S]*?)(?=\n## |\n$)/);
111-
if (whatMatch) return firstParagraph(whatMatch[1]);
127+
if (whatMatch) return stripInlineMarkdown(firstParagraph(whatMatch[1]));
112128

113129
const ucMatch = body.match(/## 1\) Use Case\s*\n+([\s\S]*?)(?=\n## |\n$)/);
114-
if (ucMatch) return firstParagraph(ucMatch[1]);
130+
if (ucMatch) return stripInlineMarkdown(firstParagraph(ucMatch[1]));
115131

116132
const scenarioMatch = body.match(
117133
/### Scenario\s*\n+([\s\S]*?)(?=\n###|\n## |\n$)/,
118134
);
119-
if (scenarioMatch) return firstParagraph(scenarioMatch[1]);
135+
if (scenarioMatch) return stripInlineMarkdown(firstParagraph(scenarioMatch[1]));
120136

121137
for (const line of body.split("\n")) {
122138
const t = line.trim();
@@ -127,7 +143,7 @@ export function extractSummary(body, type) {
127143
!t.startsWith("|") &&
128144
!t.startsWith("*")
129145
) {
130-
return t;
146+
return stripInlineMarkdown(t);
131147
}
132148
}
133149
return "";

src/layouts/Guide.astro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const navItems = [
4444
<title>{fullTitle}</title>
4545
{description && <meta name="description" content={description} />}
4646
<link rel="canonical" href={canonicalUrl} />
47+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
4748

4849
{/* Open Graph */}
4950
<meta property="og:title" content={fullTitle} />

src/layouts/Layout.astro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const base = import.meta.env.BASE_URL;
1111
<meta charset="UTF-8" />
1212
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
1313
<title>{title}</title>
14+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
1415
<style is:global>
1516
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
1617

src/pages/index.astro

Lines changed: 12 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
import Guide from '../layouts/Guide.astro';
23
import { getNodesByType, getApproachesForDomain } from '../lib/data';
34
import { getRecentPosts, formatPostDate } from '../lib/posts';
45
@@ -25,27 +26,9 @@ function contextBadge(ctx?: string) {
2526
}
2627
---
2728

28-
<html lang="en">
29-
<head>
30-
<meta charset="UTF-8">
31-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
32-
<title>IPTF — Institutional Privacy on Ethereum</title>
33-
<link rel="preconnect" href="https://fonts.googleapis.com">
34-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
35-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;1,6..72,400;1,6..72,500&display=swap" rel="stylesheet">
36-
</head>
37-
<body>
38-
39-
<!-- NAV -->
40-
<header class="nav">
41-
<div class="nav-inner">
42-
<a href="/" class="nav-brand"><div class="nav-mark"></div> IPTF</a>
43-
<div class="nav-links">
44-
<a href="/about">About</a>
45-
<a href="/blog">Blog</a>
46-
</div>
47-
</div>
48-
</header>
29+
<Guide
30+
title="IPTF — Institutional Privacy on Ethereum"
31+
description="From requirements to deployable privacy solutions for institutions on Ethereum.">
4932

5033
<!-- HERO -->
5134
<section class="hero">
@@ -93,36 +76,6 @@ function contextBadge(ctx?: string) {
9376
</div>
9477
</section>
9578

96-
<!-- TESTIMONIALS -->
97-
<section class="testimonials">
98-
<div class="test-inner">
99-
<div class="test-label">What institutions tell us</div>
100-
<div class="test-grid">
101-
<div class="test-card">
102-
<p class="test-quote">"We spent six months evaluating privacy solutions before finding IPTF. Their map saved us from three vendor pitches that didn't match our compliance requirements."</p>
103-
<div class="test-attr">
104-
<div class="test-avatar">HD</div>
105-
<div><div class="test-name">Head of Digital Assets</div><div class="test-role">European Asset Manager</div></div>
106-
</div>
107-
</div>
108-
<div class="test-card">
109-
<p class="test-quote">"The CROPS framework gave our risk team a common language to evaluate privacy architectures. We use it in every infrastructure decision now."</p>
110-
<div class="test-attr">
111-
<div class="test-avatar">RA</div>
112-
<div><div class="test-name">Risk Analyst</div><div class="test-role">Global Custodian</div></div>
113-
</div>
114-
</div>
115-
<div class="test-card">
116-
<p class="test-quote">"Most privacy research is either too academic or too marketing. IPTF is the only resource that tells us what actually works and what doesn't."</p>
117-
<div class="test-attr">
118-
<div class="test-avatar">CT</div>
119-
<div><div class="test-name">CTO</div><div class="test-role">Digital Securities Platform</div></div>
120-
</div>
121-
</div>
122-
</div>
123-
</div>
124-
</section>
125-
12679
<!-- BRIDGE -->
12780
<div class="bridge"></div>
12881

@@ -206,21 +159,21 @@ function contextBadge(ctx?: string) {
206159
<div class="eval-grid">
207160
<div class="eval-card">
208161
<h3>CROPS Framework</h3>
209-
<p>Every pattern and vendor rated on four dimensions.</p>
162+
<p>Every pattern and vendor rated on Censorship Resistance, Open Source, Privacy, and Security.</p>
210163
<div class="crops-bar">
211-
<div class="crop crop-g"><div class="crop-l">CR</div><div class="crop-v">High</div></div>
212-
<div class="crop crop-a"><div class="crop-l">OS</div><div class="crop-v">Partial</div></div>
213-
<div class="crop crop-g"><div class="crop-l">P</div><div class="crop-v">Full</div></div>
214-
<div class="crop crop-a"><div class="crop-l">S</div><div class="crop-v">Medium</div></div>
164+
<div class="crop crop-g" title="Censorship Resistance"><div class="crop-l">CR</div><div class="crop-v">High</div></div>
165+
<div class="crop crop-a" title="Open Source and Free"><div class="crop-l">OS</div><div class="crop-v">Partial</div></div>
166+
<div class="crop crop-g" title="Privacy"><div class="crop-l">P</div><div class="crop-v">Full</div></div>
167+
<div class="crop crop-a" title="Security"><div class="crop-l">S</div><div class="crop-v">Medium</div></div>
215168
</div>
216169
</div>
217170
<div class="eval-div"></div>
218171
<div class="eval-card">
219172
<h3>I2I vs I2U Context</h3>
220173
<p>Privacy requirements differ by power dynamics.</p>
221174
<div class="ctx-pair">
222-
<div class="ctx-box ctx-i"><strong>I2I</strong><small>Symmetric trust between institutions</small></div>
223-
<div class="ctx-box ctx-u"><strong>I2U</strong><small>Asymmetric. CROPS protects the user</small></div>
175+
<div class="ctx-box ctx-i"><strong>I2I</strong><small>Institution-to-Institution. Symmetric trust between counterparties.</small></div>
176+
<div class="ctx-box ctx-u"><strong>I2U</strong><small>Institution-to-End-User. Asymmetric; CROPS protects the user.</small></div>
224177
</div>
225178
</div>
226179
</div>
@@ -261,20 +214,6 @@ function contextBadge(ctx?: string) {
261214
)}
262215
</div>
263216

264-
<!-- FOOTER -->
265-
<footer class="footer">
266-
<div class="footer-inner">
267-
<div class="footer-left"><div class="footer-mark"></div> IPTF &middot; Ethereum Foundation &middot; 2026</div>
268-
<div class="footer-links">
269-
<a href="/about">About</a>
270-
<a href="/explore/galaxy">Explorer</a>
271-
<a href="https://github.com/ethereum/iptf-map">GitHub</a>
272-
<a href="/blog">Blog</a>
273-
<a href="mailto:iptf@ethereum.org">Contact</a>
274-
</div>
275-
</div>
276-
</footer>
277-
278217
<script is:inline>
279218
(function() {
280219
const el = document.querySelector('.cycle');
@@ -293,8 +232,7 @@ function contextBadge(ctx?: string) {
293232
})();
294233
</script>
295234

296-
</body>
297-
</html>
235+
</Guide>
298236

299237
<style is:global>
300238
:root {
@@ -332,48 +270,6 @@ function contextBadge(ctx?: string) {
332270
overflow-x: hidden;
333271
}
334272

335-
/* Grain */
336-
body::before {
337-
content: '';
338-
position: fixed;
339-
inset: 0;
340-
z-index: 9999;
341-
pointer-events: none;
342-
opacity: 0.03;
343-
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
344-
background-repeat: repeat;
345-
background-size: 200px;
346-
}
347-
348-
/* -- NAV -- */
349-
.nav {
350-
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
351-
background: rgba(8,8,10,0.75);
352-
backdrop-filter: blur(24px) saturate(180%);
353-
-webkit-backdrop-filter: blur(24px) saturate(180%);
354-
border-bottom: 1px solid rgba(255,255,255,0.05);
355-
}
356-
.nav-inner {
357-
max-width: 1120px; margin: 0 auto; padding: 0 2rem;
358-
height: 56px; display: flex; align-items: center; justify-content: space-between;
359-
}
360-
.nav-brand {
361-
display: flex; align-items: center; gap: 10px;
362-
text-decoration: none; color: var(--white);
363-
font-weight: 700; font-size: 0.85rem; letter-spacing: 0.1em;
364-
}
365-
.nav-mark {
366-
width: 18px; height: 18px;
367-
background: linear-gradient(135deg, var(--indigo), var(--indigo-deep));
368-
transform: rotate(45deg); border-radius: 3px;
369-
}
370-
.nav-links { display: flex; align-items: center; gap: 1.75rem; }
371-
.nav-links a {
372-
text-decoration: none; color: var(--gray-500);
373-
font-size: 0.8rem; font-weight: 500; transition: color 0.15s;
374-
}
375-
.nav-links a:hover { color: var(--white); }
376-
377273
/* -- HERO -- */
378274
.hero {
379275
min-height: 100vh;
@@ -533,40 +429,6 @@ function contextBadge(ctx?: string) {
533429
font-size: 0.85rem; color: var(--gray-500); line-height: 1.6;
534430
}
535431

536-
/* -- TESTIMONIALS -- */
537-
.testimonials {
538-
padding: 5rem 2rem;
539-
border-top: 1px solid rgba(255,255,255,0.04);
540-
}
541-
.test-inner { max-width: 1120px; margin: 0 auto; }
542-
.test-label {
543-
font-size: 0.65rem; font-weight: 600; color: var(--gray-600);
544-
letter-spacing: 0.12em; text-transform: uppercase;
545-
text-align: center; margin-bottom: 3rem;
546-
}
547-
.test-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
548-
.test-card {
549-
padding: 1.75rem;
550-
border: 1px solid rgba(255,255,255,0.06);
551-
border-radius: 12px;
552-
background: rgba(255,255,255,0.02);
553-
}
554-
.test-quote {
555-
font-family: 'Newsreader', Georgia, serif;
556-
font-size: 1rem; font-style: italic;
557-
color: var(--gray-300); line-height: 1.6;
558-
margin-bottom: 1.25rem;
559-
}
560-
.test-attr { display: flex; align-items: center; gap: 10px; }
561-
.test-avatar {
562-
width: 32px; height: 32px; border-radius: 50%;
563-
background: var(--dark-3);
564-
display: flex; align-items: center; justify-content: center;
565-
font-size: 0.7rem; font-weight: 700; color: var(--gray-500);
566-
}
567-
.test-name { font-size: 0.8rem; font-weight: 600; color: var(--gray-300); }
568-
.test-role { font-size: 0.75rem; color: var(--gray-600); }
569-
570432
/* -- BRIDGE -- */
571433
.bridge { display: none; }
572434

@@ -786,35 +648,10 @@ function contextBadge(ctx?: string) {
786648
.ctx-u strong { color: #fbbf24; }
787649
.ctx-box small { font-size: 0.72rem; color: var(--gray-400); line-height: 1.3; }
788650

789-
/* -- FOOTER -- */
790-
.footer {
791-
padding: 2.5rem 2rem; background: var(--black);
792-
border-top: 1px solid rgba(255,255,255,0.05);
793-
}
794-
.footer-inner {
795-
max-width: 1120px; margin: 0 auto;
796-
display: flex; align-items: center; justify-content: space-between;
797-
}
798-
.footer-left {
799-
display: flex; align-items: center; gap: 8px;
800-
font-size: 0.75rem; color: var(--gray-600);
801-
}
802-
.footer-mark {
803-
width: 12px; height: 12px; background: var(--dark-4);
804-
transform: rotate(45deg); border-radius: 2px;
805-
}
806-
.footer-links { display: flex; gap: 1.5rem; }
807-
.footer-links a {
808-
font-size: 0.75rem; color: var(--gray-600);
809-
text-decoration: none; transition: color 0.15s;
810-
}
811-
.footer-links a:hover { color: var(--gray-400); }
812-
813651
/* -- RESPONSIVE -- */
814652
@media (max-width: 1024px) {
815653
.pos-line-h { width: 40px; margin: 0 0.5rem; }
816654
.pos-pillars { grid-template-columns: 1fr; gap: 1.5rem; }
817-
.test-grid { grid-template-columns: 1fr; gap: 1rem; }
818655
.dom-grid { grid-template-columns: repeat(2, 1fr); }
819656
}
820657
@media (max-width: 640px) {

0 commit comments

Comments
 (0)