portable-agent-layer 0.30.1 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/skills/consulting-report/SKILL.md +74 -74
- package/assets/skills/consulting-report/demo/app/globals.css +459 -0
- package/assets/skills/consulting-report/demo/app/layout.tsx +32 -0
- package/assets/skills/consulting-report/demo/app/page.tsx +302 -0
- package/assets/skills/consulting-report/demo/bun.lock +240 -0
- package/assets/skills/consulting-report/demo/components/callout.tsx +13 -0
- package/assets/skills/consulting-report/demo/components/comparison-table.tsx +40 -0
- package/assets/skills/consulting-report/demo/components/cover-page.tsx +34 -0
- package/assets/skills/consulting-report/demo/components/exhibit.tsx +21 -0
- package/assets/skills/consulting-report/demo/components/finding-card.tsx +28 -0
- package/assets/skills/consulting-report/demo/components/quote-block.tsx +17 -0
- package/assets/skills/consulting-report/demo/components/recommendation-card.tsx +52 -0
- package/assets/skills/consulting-report/demo/components/section.tsx +17 -0
- package/assets/skills/consulting-report/demo/components/severity-badge.tsx +19 -0
- package/assets/skills/consulting-report/demo/components/stat-grid.tsx +26 -0
- package/assets/skills/consulting-report/demo/components/table-of-contents.tsx +27 -0
- package/assets/skills/consulting-report/demo/components/timeline.tsx +20 -0
- package/assets/skills/consulting-report/demo/lib/report-data.ts +247 -0
- package/assets/skills/consulting-report/demo/lib/utils.ts +6 -0
- package/assets/skills/consulting-report/demo/next.config.js +9 -0
- package/assets/skills/consulting-report/demo/package.json +27 -0
- package/assets/skills/consulting-report/demo/postcss.config.mjs +5 -0
- package/assets/skills/consulting-report/demo/tsconfig.json +41 -0
- package/assets/skills/consulting-report/template/app/globals.css +459 -0
- package/assets/skills/consulting-report/template/app/layout.tsx +32 -0
- package/assets/skills/consulting-report/template/app/page.tsx +282 -0
- package/assets/skills/consulting-report/template/bun.lock +240 -0
- package/assets/skills/consulting-report/template/components/callout.tsx +13 -0
- package/assets/skills/consulting-report/template/components/comparison-table.tsx +40 -0
- package/assets/skills/consulting-report/template/components/cover-page.tsx +34 -0
- package/assets/skills/consulting-report/template/components/exhibit.tsx +21 -0
- package/assets/skills/consulting-report/template/components/finding-card.tsx +28 -0
- package/assets/skills/consulting-report/template/components/quote-block.tsx +17 -0
- package/assets/skills/consulting-report/template/components/recommendation-card.tsx +52 -0
- package/assets/skills/consulting-report/template/components/section.tsx +17 -0
- package/assets/skills/consulting-report/template/components/severity-badge.tsx +19 -0
- package/assets/skills/consulting-report/template/components/stat-grid.tsx +26 -0
- package/assets/skills/consulting-report/template/components/table-of-contents.tsx +27 -0
- package/assets/skills/consulting-report/template/components/timeline.tsx +20 -0
- package/assets/skills/consulting-report/template/lib/report-data.ts +176 -0
- package/assets/skills/consulting-report/template/lib/utils.ts +6 -0
- package/assets/skills/consulting-report/template/next.config.js +9 -0
- package/assets/skills/consulting-report/template/package.json +27 -0
- package/assets/skills/consulting-report/template/postcss.config.mjs +5 -0
- package/assets/skills/consulting-report/template/tsconfig.json +27 -0
- package/assets/skills/consulting-report/tools/dev.ts +47 -0
- package/assets/skills/consulting-report/tools/generate-pdf.ts +140 -408
- package/assets/skills/consulting-report/tools/scaffold.ts +83 -48
- package/assets/skills/presentation/SKILL.md +1 -1
- package/package.json +9 -9
- package/assets/skills/consulting-report/demo/content/current-state.md +0 -33
- package/assets/skills/consulting-report/demo/content/executive-summary.md +0 -19
- package/assets/skills/consulting-report/demo/content/report-data.ts +0 -101
- package/assets/skills/consulting-report/demo/diagrams/.gitkeep +0 -0
- package/assets/skills/consulting-report/template/README.md +0 -28
- package/assets/skills/consulting-report/template/content/executive-summary.md +0 -19
- package/assets/skills/consulting-report/template/content/report-data.ts +0 -59
- package/assets/skills/consulting-report/template/diagrams/.gitkeep +0 -0
|
@@ -1,115 +1,115 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: consulting-report
|
|
3
|
-
description:
|
|
4
|
-
argument-hint: <
|
|
3
|
+
description: Build a beautifully-typeset consulting-report PDF from a typed data file and a React layout. Use when generating an assessment, strategic review, operational readiness check, or any McKinsey-style consulting deliverable as a PDF.
|
|
4
|
+
argument-hint: scaffold <target-dir> | dev <report-dir> | <report-dir> (render PDF)
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Each report lives in its own directory with data (TypeScript) + narrative (Markdown) + diagrams (images). The skill provides a scaffolder to spin up new reports from a template and a generator to render them.
|
|
12
|
-
|
|
13
|
-
**Default brand:** Konvert7. Override per report via the `brand` block in `report-data.ts`.
|
|
14
|
-
|
|
15
|
-
## Report Directory Layout
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
<report-dir>/
|
|
19
|
-
├── content/
|
|
20
|
-
│ ├── report-data.ts # report structure (schema: ConsultingReport)
|
|
21
|
-
│ ├── executive-summary.md # narrative sections
|
|
22
|
-
│ └── …
|
|
23
|
-
├── diagrams/ # source images (PNG/JPG)
|
|
24
|
-
├── diagrams-compressed/ # generated — ignore
|
|
25
|
-
└── <client>-<title>-<date>.{pdf,html} # output
|
|
26
|
-
```
|
|
9
|
+
Every report is a self-contained Next.js app: typed report data in `lib/report-data.ts`, layout composed from React components in `app/page.tsx`, fonts via `next/font/google` (Source Serif 4 + Inter), Tailwind v4 for styling. `bun run dev` gives a live preview while authoring; the PDF is rendered by Playwright against a static export.
|
|
27
10
|
|
|
28
11
|
## Workflow
|
|
29
12
|
|
|
30
|
-
###
|
|
13
|
+
### 1. Scaffold a new report
|
|
31
14
|
|
|
32
15
|
```bash
|
|
33
16
|
bun ~/.pal/skills/consulting-report/tools/scaffold.ts <target-dir> \
|
|
34
|
-
--client "Client Name"
|
|
35
|
-
--title "Report Title"
|
|
17
|
+
[--client "Client Name"] [--title "Report Title"] [--no-install]
|
|
36
18
|
```
|
|
37
19
|
|
|
38
|
-
Creates the
|
|
20
|
+
Creates `<target-dir>` from the template and runs `bun install` inside. If `--client` / `--title` are passed, they're stamped into `lib/report-data.ts`. The scaffolder refuses to overwrite an existing directory.
|
|
21
|
+
|
|
22
|
+
### 2. Fill in the content
|
|
39
23
|
|
|
40
|
-
|
|
24
|
+
Two files do the work:
|
|
41
25
|
|
|
42
|
-
|
|
26
|
+
- **`lib/report-data.ts`** — typed metadata: client, title, date, classification, consultancy name, executive summary, situation assessment, findings, risk analysis, strategic opportunity, recommendations, target state, roadmap, call to action. The `ReportData` interface guides what's required.
|
|
27
|
+
- **`app/page.tsx`** — the layout. Composes `<CoverPage/>`, `<Section/>`, `<Exhibit/>`, `<FindingCard/>`, `<RecommendationCard/>`, `<Callout/>`, `<Timeline/>`, etc. against the data. Edit freely — section titles, intros, ordering, custom JSX, anything.
|
|
43
28
|
|
|
44
|
-
|
|
45
|
-
- `<dir>/content/*.md` — the narrative sections referenced from `report-data.ts`.
|
|
46
|
-
- `<dir>/diagrams/` — drop PNG/JPG images. Reference them from markdown with relative paths, e.g. ``.
|
|
29
|
+
Static images go in `public/`; reference them from JSX as `<img src="/your-image.png">`.
|
|
47
30
|
|
|
48
|
-
###
|
|
31
|
+
### 3. Live preview while authoring
|
|
49
32
|
|
|
50
33
|
```bash
|
|
51
|
-
|
|
34
|
+
bun ~/.pal/skills/consulting-report/tools/dev.ts <report-dir>
|
|
52
35
|
```
|
|
53
36
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
The generator:
|
|
57
|
-
- Loads `content/report-data.ts` dynamically
|
|
58
|
-
- Compresses `diagrams/*` to JPEG 70% / 1200px via `sips` (macOS); silently skips if `sips` is absent
|
|
59
|
-
- Renders cover, auto-generated linked TOC, sections, findings, recommendations, conclusion, appendix
|
|
60
|
-
- Prints via Playwright with page-numbered header/footer templates
|
|
37
|
+
Wraps `bun run dev` in the report directory. Open the URL printed by Next, edit `app/page.tsx` or `lib/report-data.ts`, browser hot-reloads.
|
|
61
38
|
|
|
62
|
-
###
|
|
39
|
+
### 4. Render the PDF
|
|
63
40
|
|
|
64
41
|
```bash
|
|
65
|
-
|
|
42
|
+
node --experimental-strip-types ~/.pal/skills/consulting-report/tools/generate-pdf.ts <report-dir>
|
|
66
43
|
```
|
|
67
44
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
interface ConsultingReport {
|
|
74
|
-
clientName: string;
|
|
75
|
-
reportTitle: string;
|
|
76
|
-
reportDate: string;
|
|
77
|
-
classification: string; // e.g., "CONFIDENTIAL"
|
|
78
|
-
version: string;
|
|
79
|
-
brand?: { businessName: string; brandLabel?: string; logoPath?: string; };
|
|
80
|
-
sections: Section[];
|
|
81
|
-
findings?: Finding[]; // renders as red/amber/blue boxes by severity
|
|
82
|
-
recommendations?: Recommendation[]; // blue boxes with priority badges
|
|
83
|
-
conclusion?: Conclusion;
|
|
84
|
-
supportingEvidence?: Record<string, string[]>; // appendix
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
interface Section { id: string; title: string; content: string; subsections?: Section[]; }
|
|
88
|
-
interface Finding { id: string; title: string; severity: "critical"|"high"|"medium"|"low"; evidence: string; impact?: string; }
|
|
89
|
-
interface Recommendation { id: string; title: string; priority: "immediate"|"short-term"|"long-term"; detail: string; owner?: string; }
|
|
90
|
-
interface Conclusion { assessorNote?: string; contextNote?: string; closingRemarks?: string; }
|
|
45
|
+
Runs `next build` (which produces a static export at `out/`), then Playwright loads it via a tiny in-process HTTP server and prints the PDF with page-numbered header/footer. Output:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
<report-dir>/<client-slug>-<title-slug>-<date>.pdf
|
|
91
49
|
```
|
|
92
50
|
|
|
93
|
-
|
|
51
|
+
Override with `--pdf <path>`. Pass `--skip-build` to re-render the PDF from the existing `out/` without re-building.
|
|
94
52
|
|
|
95
|
-
|
|
53
|
+
Run with **Node**, not Bun — Playwright's `chromium.launch()` hangs under Bun on Windows.
|
|
96
54
|
|
|
97
|
-
|
|
55
|
+
## Directory Layout
|
|
98
56
|
|
|
99
|
-
|
|
57
|
+
```
|
|
58
|
+
<report-dir>/
|
|
59
|
+
├── app/
|
|
60
|
+
│ ├── layout.tsx # font wiring (Inter + Source Serif 4)
|
|
61
|
+
│ ├── page.tsx # the report's layout — edit freely
|
|
62
|
+
│ └── globals.css # design tokens via @theme + custom CSS
|
|
63
|
+
├── components/ # report primitives — edit if you need new shapes
|
|
64
|
+
│ ├── cover-page.tsx
|
|
65
|
+
│ ├── table-of-contents.tsx
|
|
66
|
+
│ ├── section.tsx
|
|
67
|
+
│ ├── exhibit.tsx
|
|
68
|
+
│ ├── stat-grid.tsx
|
|
69
|
+
│ ├── comparison-table.tsx
|
|
70
|
+
│ ├── finding-card.tsx
|
|
71
|
+
│ ├── recommendation-card.tsx
|
|
72
|
+
│ ├── severity-badge.tsx
|
|
73
|
+
│ ├── callout.tsx
|
|
74
|
+
│ ├── quote-block.tsx
|
|
75
|
+
│ └── timeline.tsx
|
|
76
|
+
├── lib/
|
|
77
|
+
│ ├── report-data.ts # all your content
|
|
78
|
+
│ └── utils.ts
|
|
79
|
+
├── public/ # static images, optional
|
|
80
|
+
├── package.json # exact-pinned: next, react, tailwindcss, etc.
|
|
81
|
+
├── tsconfig.json
|
|
82
|
+
├── next.config.js
|
|
83
|
+
└── postcss.config.mjs
|
|
84
|
+
```
|
|
100
85
|
|
|
101
|
-
|
|
86
|
+
## Component Cheatsheet
|
|
87
|
+
|
|
88
|
+
- `<CoverPage clientName reportTitle reportDate classification consultancyName preTitle />` — full-bleed cover
|
|
89
|
+
- `<TableOfContents items={[{id, title}, …]} title?>` — linked TOC, anchors to section IDs
|
|
90
|
+
- `<Section id title>` — top-level section with bottom-rule heading; `id` enables TOC links
|
|
91
|
+
- `<Exhibit number title source?>` — bordered card for figures, tables, side data
|
|
92
|
+
- `<StatGrid stats={[{value, label, caption?}, …]} />` — large-number grid for executive summary
|
|
93
|
+
- `<ComparisonTable leftLabel rightLabel rows={[{metric, left, right}, …]} metricLabel?>` — current vs. target side-by-side
|
|
94
|
+
- `<FindingCard finding={f} index={i} />` — driven by `Finding` type, includes severity badge
|
|
95
|
+
- `<RecommendationCard recommendation={r} index={i} />` — driven by `Recommendation` type, includes priority badge
|
|
96
|
+
- `<Callout label?>` — left-rule emphasis block (default label "Key Takeaway")
|
|
97
|
+
- `<QuoteBlock quote attribution role? />` — pull-quote with serif quote mark
|
|
98
|
+
- `<Timeline phases={r.roadmap} />` — vertical timeline with dotted line
|
|
99
|
+
- `<SeverityBadge severity />` — pill badge: critical / high / medium / low
|
|
100
|
+
|
|
101
|
+
## Demo
|
|
102
102
|
|
|
103
103
|
```bash
|
|
104
104
|
node --experimental-strip-types ~/.pal/skills/consulting-report/tools/generate-pdf.ts ~/.pal/skills/consulting-report/demo
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
Inspect the
|
|
107
|
+
Renders the bundled Acme Industries example end-to-end. Inspect the resulting PDF to see the full layout before authoring your own.
|
|
108
108
|
|
|
109
109
|
## Important
|
|
110
110
|
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
111
|
+
- Run on Node ≥ 22.6 (Playwright + `--experimental-strip-types`)
|
|
112
|
+
- Bundled fonts come from Google Fonts via `next/font/google` — no licensing surface, no CDN at runtime, glyphs embedded at build time
|
|
113
|
+
- Reports are disposable artifacts of `lib/report-data.ts` + `app/page.tsx`; commit the source, not the PDF
|
|
114
|
+
- The scaffolder runs `bun install` inside the target by default — pass `--no-install` to skip
|
|
115
|
+
- Header/footer templates render with `displayHeaderFooter: true` in Playwright; don't combine with CSS `@page` margin-box rules
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@theme {
|
|
4
|
+
--color-background: #ffffff;
|
|
5
|
+
--color-background-secondary: #f8fafc;
|
|
6
|
+
--color-background-tertiary: #f1f5f9;
|
|
7
|
+
--color-background-elevated: #e2e8f0;
|
|
8
|
+
|
|
9
|
+
--color-foreground: #0f172a;
|
|
10
|
+
--color-muted: #475569;
|
|
11
|
+
--color-muted-dark: #94a3b8;
|
|
12
|
+
|
|
13
|
+
--color-border: #e2e8f0;
|
|
14
|
+
--color-border-subtle: #f1f5f9;
|
|
15
|
+
--color-border-emphasis: #cbd5e1;
|
|
16
|
+
|
|
17
|
+
--color-primary: #1d4ed8;
|
|
18
|
+
--color-accent: #7c3aed;
|
|
19
|
+
--color-success: #16a34a;
|
|
20
|
+
--color-warning: #d97706;
|
|
21
|
+
--color-destructive: #dc2626;
|
|
22
|
+
|
|
23
|
+
--color-section: #f8fafc;
|
|
24
|
+
--color-callout: #eff6ff;
|
|
25
|
+
|
|
26
|
+
--font-sans: var(--font-inter), system-ui, sans-serif;
|
|
27
|
+
--font-serif: var(--font-source-serif), Georgia, serif;
|
|
28
|
+
--font-heading: var(--font-inter), system-ui, sans-serif;
|
|
29
|
+
--font-body: var(--font-source-serif), Georgia, serif;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@source "./**/*.{ts,tsx}";
|
|
33
|
+
@source "../components/**/*.{ts,tsx}";
|
|
34
|
+
|
|
35
|
+
body {
|
|
36
|
+
font-family: var(--font-body);
|
|
37
|
+
background: var(--color-background);
|
|
38
|
+
color: var(--color-foreground);
|
|
39
|
+
line-height: 1.7;
|
|
40
|
+
-webkit-font-smoothing: antialiased;
|
|
41
|
+
-moz-osx-font-smoothing: grayscale;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* McKinsey-style report layout */
|
|
45
|
+
.report-container {
|
|
46
|
+
max-width: 850px;
|
|
47
|
+
margin: 0 auto;
|
|
48
|
+
padding: 2rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.report-section {
|
|
52
|
+
margin-bottom: 3rem;
|
|
53
|
+
page-break-inside: avoid;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.report-section h2 {
|
|
57
|
+
font-family: var(--font-heading);
|
|
58
|
+
font-size: 1.75rem;
|
|
59
|
+
font-weight: 600;
|
|
60
|
+
color: var(--color-foreground);
|
|
61
|
+
margin-bottom: 1.5rem;
|
|
62
|
+
padding-bottom: 0.5rem;
|
|
63
|
+
border-bottom: 2px solid var(--color-primary);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.report-section h3 {
|
|
67
|
+
font-family: var(--font-heading);
|
|
68
|
+
font-size: 1.25rem;
|
|
69
|
+
font-weight: 600;
|
|
70
|
+
color: var(--color-foreground);
|
|
71
|
+
margin-bottom: 1rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Exhibit */
|
|
75
|
+
.exhibit {
|
|
76
|
+
background: var(--color-background-secondary);
|
|
77
|
+
border: 1px solid var(--color-border);
|
|
78
|
+
border-radius: 0.5rem;
|
|
79
|
+
padding: 1.5rem;
|
|
80
|
+
margin: 1.5rem 0;
|
|
81
|
+
}
|
|
82
|
+
.exhibit-header {
|
|
83
|
+
display: flex;
|
|
84
|
+
justify-content: space-between;
|
|
85
|
+
align-items: baseline;
|
|
86
|
+
margin-bottom: 1rem;
|
|
87
|
+
padding-bottom: 0.5rem;
|
|
88
|
+
border-bottom: 1px solid var(--color-border-subtle);
|
|
89
|
+
}
|
|
90
|
+
.exhibit-number {
|
|
91
|
+
font-family: var(--font-sans);
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
color: var(--color-primary);
|
|
94
|
+
font-size: 0.875rem;
|
|
95
|
+
text-transform: uppercase;
|
|
96
|
+
letter-spacing: 0.1em;
|
|
97
|
+
}
|
|
98
|
+
.exhibit-title {
|
|
99
|
+
font-family: var(--font-heading);
|
|
100
|
+
font-weight: 600;
|
|
101
|
+
color: var(--color-foreground);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Callout */
|
|
105
|
+
.callout {
|
|
106
|
+
background: var(--color-callout);
|
|
107
|
+
border-left: 4px solid var(--color-primary);
|
|
108
|
+
padding: 1.25rem 1.5rem;
|
|
109
|
+
margin: 1.5rem 0;
|
|
110
|
+
border-radius: 0 0.5rem 0.5rem 0;
|
|
111
|
+
}
|
|
112
|
+
.callout-label {
|
|
113
|
+
font-family: var(--font-sans);
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
color: var(--color-primary);
|
|
116
|
+
font-size: 0.75rem;
|
|
117
|
+
text-transform: uppercase;
|
|
118
|
+
letter-spacing: 0.1em;
|
|
119
|
+
margin-bottom: 0.5rem;
|
|
120
|
+
}
|
|
121
|
+
.callout-content {
|
|
122
|
+
font-size: 1.125rem;
|
|
123
|
+
font-weight: 500;
|
|
124
|
+
color: var(--color-foreground);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Quote block */
|
|
128
|
+
.quote-block {
|
|
129
|
+
position: relative;
|
|
130
|
+
padding: 1.5rem 2rem;
|
|
131
|
+
margin: 1.5rem 0;
|
|
132
|
+
background: var(--color-background-secondary);
|
|
133
|
+
border-radius: 0.5rem;
|
|
134
|
+
border: 1px solid var(--color-border-subtle);
|
|
135
|
+
}
|
|
136
|
+
.quote-block::before {
|
|
137
|
+
content: "\201C";
|
|
138
|
+
position: absolute;
|
|
139
|
+
top: 0.5rem;
|
|
140
|
+
left: 0.75rem;
|
|
141
|
+
font-size: 3rem;
|
|
142
|
+
color: var(--color-primary);
|
|
143
|
+
opacity: 0.5;
|
|
144
|
+
font-family: Georgia, serif;
|
|
145
|
+
line-height: 1;
|
|
146
|
+
}
|
|
147
|
+
.quote-text {
|
|
148
|
+
font-style: italic;
|
|
149
|
+
color: var(--color-foreground);
|
|
150
|
+
font-size: 1.0625rem;
|
|
151
|
+
line-height: 1.7;
|
|
152
|
+
}
|
|
153
|
+
.quote-attribution {
|
|
154
|
+
margin-top: 0.75rem;
|
|
155
|
+
font-size: 0.875rem;
|
|
156
|
+
color: var(--color-muted);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Severity badges */
|
|
160
|
+
.severity-badge {
|
|
161
|
+
display: inline-flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
padding: 0.25rem 0.75rem;
|
|
164
|
+
border-radius: 9999px;
|
|
165
|
+
font-family: var(--font-sans);
|
|
166
|
+
font-size: 0.75rem;
|
|
167
|
+
font-weight: 600;
|
|
168
|
+
text-transform: uppercase;
|
|
169
|
+
letter-spacing: 0.05em;
|
|
170
|
+
}
|
|
171
|
+
.severity-critical {
|
|
172
|
+
background: rgba(220, 38, 38, 0.1);
|
|
173
|
+
color: var(--color-destructive);
|
|
174
|
+
border: 1px solid rgba(220, 38, 38, 0.3);
|
|
175
|
+
}
|
|
176
|
+
.severity-high {
|
|
177
|
+
background: rgba(234, 88, 12, 0.1);
|
|
178
|
+
color: #ea580c;
|
|
179
|
+
border: 1px solid rgba(234, 88, 12, 0.3);
|
|
180
|
+
}
|
|
181
|
+
.severity-medium {
|
|
182
|
+
background: rgba(217, 119, 6, 0.1);
|
|
183
|
+
color: var(--color-warning);
|
|
184
|
+
border: 1px solid rgba(217, 119, 6, 0.3);
|
|
185
|
+
}
|
|
186
|
+
.severity-low {
|
|
187
|
+
background: rgba(22, 163, 74, 0.1);
|
|
188
|
+
color: var(--color-success);
|
|
189
|
+
border: 1px solid rgba(22, 163, 74, 0.3);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* Finding card */
|
|
193
|
+
.finding-card {
|
|
194
|
+
background: var(--color-background-secondary);
|
|
195
|
+
border: 1px solid var(--color-border);
|
|
196
|
+
border-radius: 0.5rem;
|
|
197
|
+
padding: 1.5rem;
|
|
198
|
+
margin-bottom: 1rem;
|
|
199
|
+
}
|
|
200
|
+
.finding-header {
|
|
201
|
+
display: flex;
|
|
202
|
+
justify-content: space-between;
|
|
203
|
+
align-items: flex-start;
|
|
204
|
+
margin-bottom: 0.75rem;
|
|
205
|
+
}
|
|
206
|
+
.finding-title {
|
|
207
|
+
font-family: var(--font-heading);
|
|
208
|
+
font-weight: 600;
|
|
209
|
+
color: var(--color-foreground);
|
|
210
|
+
font-size: 1.0625rem;
|
|
211
|
+
}
|
|
212
|
+
.finding-evidence {
|
|
213
|
+
font-size: 0.9375rem;
|
|
214
|
+
color: var(--color-muted);
|
|
215
|
+
margin-top: 0.5rem;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* Timeline */
|
|
219
|
+
.timeline {
|
|
220
|
+
position: relative;
|
|
221
|
+
padding-left: 2rem;
|
|
222
|
+
}
|
|
223
|
+
.timeline::before {
|
|
224
|
+
content: "";
|
|
225
|
+
position: absolute;
|
|
226
|
+
left: 0.5rem;
|
|
227
|
+
top: 0;
|
|
228
|
+
bottom: 0;
|
|
229
|
+
width: 2px;
|
|
230
|
+
background: linear-gradient(180deg, var(--color-primary) 0%, var(--color-accent) 100%);
|
|
231
|
+
}
|
|
232
|
+
.timeline-item {
|
|
233
|
+
position: relative;
|
|
234
|
+
padding-bottom: 1.5rem;
|
|
235
|
+
}
|
|
236
|
+
.timeline-item::before {
|
|
237
|
+
content: "";
|
|
238
|
+
position: absolute;
|
|
239
|
+
/* Center dot on the vertical line: line is at left:0.5rem (8px) +1px,
|
|
240
|
+
timeline-item starts at padding 2rem (32px). Dot half-width 6px → place
|
|
241
|
+
left at -1.8125rem so dot center lands at 9px from .timeline left. */
|
|
242
|
+
left: -1.8125rem;
|
|
243
|
+
top: 0.4rem;
|
|
244
|
+
width: 0.75rem;
|
|
245
|
+
height: 0.75rem;
|
|
246
|
+
border-radius: 50%;
|
|
247
|
+
background: var(--color-primary);
|
|
248
|
+
}
|
|
249
|
+
.timeline-phase {
|
|
250
|
+
font-family: var(--font-sans);
|
|
251
|
+
font-weight: 600;
|
|
252
|
+
color: var(--color-primary);
|
|
253
|
+
font-size: 0.875rem;
|
|
254
|
+
text-transform: uppercase;
|
|
255
|
+
letter-spacing: 0.1em;
|
|
256
|
+
}
|
|
257
|
+
.timeline-title {
|
|
258
|
+
font-family: var(--font-heading);
|
|
259
|
+
font-weight: 600;
|
|
260
|
+
color: var(--color-foreground);
|
|
261
|
+
margin-top: 0.25rem;
|
|
262
|
+
}
|
|
263
|
+
.timeline-description {
|
|
264
|
+
color: var(--color-muted);
|
|
265
|
+
font-size: 0.9375rem;
|
|
266
|
+
margin-top: 0.25rem;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* Table of Contents */
|
|
270
|
+
.toc {
|
|
271
|
+
page-break-after: always;
|
|
272
|
+
margin-bottom: 3rem;
|
|
273
|
+
}
|
|
274
|
+
.toc h2 {
|
|
275
|
+
font-family: var(--font-heading);
|
|
276
|
+
font-size: 1.75rem;
|
|
277
|
+
font-weight: 600;
|
|
278
|
+
color: var(--color-foreground);
|
|
279
|
+
margin-bottom: 1.5rem;
|
|
280
|
+
padding-bottom: 0.5rem;
|
|
281
|
+
border-bottom: 2px solid var(--color-primary);
|
|
282
|
+
}
|
|
283
|
+
.toc ol {
|
|
284
|
+
list-style: none;
|
|
285
|
+
padding-left: 0;
|
|
286
|
+
margin: 0;
|
|
287
|
+
}
|
|
288
|
+
.toc li {
|
|
289
|
+
margin: 0.75rem 0;
|
|
290
|
+
border-bottom: 1px dotted var(--color-border-emphasis);
|
|
291
|
+
padding-bottom: 0.5rem;
|
|
292
|
+
}
|
|
293
|
+
.toc a {
|
|
294
|
+
display: flex;
|
|
295
|
+
gap: 1rem;
|
|
296
|
+
align-items: baseline;
|
|
297
|
+
color: var(--color-foreground);
|
|
298
|
+
text-decoration: none;
|
|
299
|
+
}
|
|
300
|
+
.toc-number {
|
|
301
|
+
font-family: var(--font-sans);
|
|
302
|
+
font-weight: 600;
|
|
303
|
+
font-size: 0.875rem;
|
|
304
|
+
color: var(--color-primary);
|
|
305
|
+
width: 2rem;
|
|
306
|
+
flex-shrink: 0;
|
|
307
|
+
}
|
|
308
|
+
.toc-title {
|
|
309
|
+
font-family: var(--font-heading);
|
|
310
|
+
font-weight: 500;
|
|
311
|
+
font-size: 1rem;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* Stat grid */
|
|
315
|
+
.stat-grid {
|
|
316
|
+
display: grid;
|
|
317
|
+
gap: 1.5rem;
|
|
318
|
+
margin: 1.5rem 0;
|
|
319
|
+
padding: 1.5rem;
|
|
320
|
+
background: var(--color-background-secondary);
|
|
321
|
+
border-radius: 0.5rem;
|
|
322
|
+
border: 1px solid var(--color-border);
|
|
323
|
+
page-break-inside: avoid;
|
|
324
|
+
}
|
|
325
|
+
.stat {
|
|
326
|
+
text-align: left;
|
|
327
|
+
}
|
|
328
|
+
.stat-value {
|
|
329
|
+
font-family: var(--font-sans);
|
|
330
|
+
font-size: 2.5rem;
|
|
331
|
+
font-weight: 700;
|
|
332
|
+
color: var(--color-primary);
|
|
333
|
+
letter-spacing: -0.02em;
|
|
334
|
+
line-height: 1;
|
|
335
|
+
}
|
|
336
|
+
.stat-label {
|
|
337
|
+
margin-top: 0.5rem;
|
|
338
|
+
font-family: var(--font-sans);
|
|
339
|
+
font-size: 0.8125rem;
|
|
340
|
+
font-weight: 600;
|
|
341
|
+
color: var(--color-foreground);
|
|
342
|
+
}
|
|
343
|
+
.stat-caption {
|
|
344
|
+
margin-top: 0.25rem;
|
|
345
|
+
font-family: var(--font-body);
|
|
346
|
+
font-size: 0.8125rem;
|
|
347
|
+
color: var(--color-muted);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* Comparison table */
|
|
351
|
+
.comparison-table {
|
|
352
|
+
width: 100%;
|
|
353
|
+
border-collapse: collapse;
|
|
354
|
+
margin: 1.5rem 0;
|
|
355
|
+
font-family: var(--font-body);
|
|
356
|
+
font-size: 0.9375rem;
|
|
357
|
+
page-break-inside: avoid;
|
|
358
|
+
}
|
|
359
|
+
.comparison-table th {
|
|
360
|
+
font-family: var(--font-sans);
|
|
361
|
+
font-size: 0.7rem;
|
|
362
|
+
font-weight: 600;
|
|
363
|
+
text-transform: uppercase;
|
|
364
|
+
letter-spacing: 0.08em;
|
|
365
|
+
color: var(--color-primary);
|
|
366
|
+
padding: 0.75rem 1rem;
|
|
367
|
+
border-bottom: 2px solid var(--color-primary);
|
|
368
|
+
text-align: left;
|
|
369
|
+
}
|
|
370
|
+
.comparison-table td {
|
|
371
|
+
padding: 0.75rem 1rem;
|
|
372
|
+
border-bottom: 1px solid var(--color-border-subtle);
|
|
373
|
+
vertical-align: top;
|
|
374
|
+
}
|
|
375
|
+
.comparison-table .metric {
|
|
376
|
+
font-family: var(--font-sans);
|
|
377
|
+
font-weight: 600;
|
|
378
|
+
color: var(--color-foreground);
|
|
379
|
+
}
|
|
380
|
+
.comparison-table tbody tr:last-child td {
|
|
381
|
+
border-bottom: none;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Cover page */
|
|
385
|
+
.cover-page {
|
|
386
|
+
min-height: 100vh;
|
|
387
|
+
display: flex;
|
|
388
|
+
flex-direction: column;
|
|
389
|
+
justify-content: center;
|
|
390
|
+
padding: 4rem;
|
|
391
|
+
page-break-after: always;
|
|
392
|
+
background: linear-gradient(180deg, var(--color-background) 0%, var(--color-background-secondary) 100%);
|
|
393
|
+
}
|
|
394
|
+
.cover-classification {
|
|
395
|
+
font-family: var(--font-sans);
|
|
396
|
+
font-size: 0.875rem;
|
|
397
|
+
font-weight: 600;
|
|
398
|
+
color: var(--color-destructive);
|
|
399
|
+
text-transform: uppercase;
|
|
400
|
+
letter-spacing: 0.15em;
|
|
401
|
+
margin-bottom: 4rem;
|
|
402
|
+
}
|
|
403
|
+
.cover-title {
|
|
404
|
+
font-family: var(--font-heading);
|
|
405
|
+
font-size: 3rem;
|
|
406
|
+
font-weight: 600;
|
|
407
|
+
color: var(--color-foreground);
|
|
408
|
+
line-height: 1.2;
|
|
409
|
+
margin-bottom: 1rem;
|
|
410
|
+
letter-spacing: -0.02em;
|
|
411
|
+
}
|
|
412
|
+
.cover-subtitle {
|
|
413
|
+
font-family: var(--font-heading);
|
|
414
|
+
font-size: 1.5rem;
|
|
415
|
+
color: var(--color-muted);
|
|
416
|
+
margin-bottom: 4rem;
|
|
417
|
+
font-weight: 400;
|
|
418
|
+
}
|
|
419
|
+
.cover-meta {
|
|
420
|
+
margin-top: auto;
|
|
421
|
+
padding-top: 1.5rem;
|
|
422
|
+
border-top: 1px solid var(--color-border);
|
|
423
|
+
}
|
|
424
|
+
.cover-date {
|
|
425
|
+
font-family: var(--font-sans);
|
|
426
|
+
font-size: 1rem;
|
|
427
|
+
color: var(--color-muted);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* Print styles — what Playwright sees */
|
|
431
|
+
@media print {
|
|
432
|
+
body {
|
|
433
|
+
font-size: 11pt;
|
|
434
|
+
}
|
|
435
|
+
.report-container {
|
|
436
|
+
max-width: none;
|
|
437
|
+
padding: 0;
|
|
438
|
+
}
|
|
439
|
+
.report-section,
|
|
440
|
+
.callout,
|
|
441
|
+
.exhibit,
|
|
442
|
+
.finding-card,
|
|
443
|
+
.quote-block {
|
|
444
|
+
break-inside: avoid;
|
|
445
|
+
}
|
|
446
|
+
.cover-page {
|
|
447
|
+
page-break-after: always;
|
|
448
|
+
}
|
|
449
|
+
a {
|
|
450
|
+
text-decoration: none;
|
|
451
|
+
color: var(--color-foreground);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* Page setup for PDF */
|
|
456
|
+
@page {
|
|
457
|
+
size: A4;
|
|
458
|
+
margin: 18mm 16mm;
|
|
459
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { Inter, Source_Serif_4 } from "next/font/google";
|
|
3
|
+
import "./globals.css";
|
|
4
|
+
|
|
5
|
+
const inter = Inter({
|
|
6
|
+
subsets: ["latin"],
|
|
7
|
+
variable: "--font-inter",
|
|
8
|
+
display: "swap",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const sourceSerif = Source_Serif_4({
|
|
12
|
+
subsets: ["latin"],
|
|
13
|
+
variable: "--font-source-serif",
|
|
14
|
+
display: "swap",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const metadata: Metadata = {
|
|
18
|
+
title: "Consulting Report",
|
|
19
|
+
description: "Strategic assessment and recommendations.",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default function RootLayout({
|
|
23
|
+
children,
|
|
24
|
+
}: Readonly<{ children: React.ReactNode }>) {
|
|
25
|
+
return (
|
|
26
|
+
<html lang="en" className={`${inter.variable} ${sourceSerif.variable}`}>
|
|
27
|
+
<body className="bg-background font-body text-foreground antialiased">
|
|
28
|
+
{children}
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
31
|
+
);
|
|
32
|
+
}
|