portable-agent-layer 0.30.1 → 0.31.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 +68 -74
- package/assets/skills/consulting-report/demo/app/globals.css +344 -0
- package/assets/skills/consulting-report/demo/app/layout.tsx +32 -0
- package/assets/skills/consulting-report/demo/app/page.tsx +255 -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/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 +16 -0
- package/assets/skills/consulting-report/demo/components/severity-badge.tsx +19 -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 +344 -0
- package/assets/skills/consulting-report/template/app/layout.tsx +32 -0
- package/assets/skills/consulting-report/template/app/page.tsx +255 -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/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 +16 -0
- package/assets/skills/consulting-report/template/components/severity-badge.tsx +19 -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,109 @@
|
|
|
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/ # 9 ported components — edit if you need new shapes
|
|
64
|
+
│ ├── cover-page.tsx
|
|
65
|
+
│ ├── section.tsx
|
|
66
|
+
│ ├── exhibit.tsx
|
|
67
|
+
│ ├── finding-card.tsx
|
|
68
|
+
│ ├── recommendation-card.tsx
|
|
69
|
+
│ ├── severity-badge.tsx
|
|
70
|
+
│ ├── callout.tsx
|
|
71
|
+
│ ├── quote-block.tsx
|
|
72
|
+
│ └── timeline.tsx
|
|
73
|
+
├── lib/
|
|
74
|
+
│ ├── report-data.ts # all your content
|
|
75
|
+
│ └── utils.ts
|
|
76
|
+
├── public/ # static images, optional
|
|
77
|
+
├── package.json # exact-pinned: next, react, tailwindcss, etc.
|
|
78
|
+
├── tsconfig.json
|
|
79
|
+
├── next.config.js
|
|
80
|
+
└── postcss.config.mjs
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Component Cheatsheet
|
|
100
84
|
|
|
101
|
-
|
|
85
|
+
- `<CoverPage clientName reportTitle reportDate classification consultancyName preTitle />` — full-bleed cover
|
|
86
|
+
- `<Section title>` — top-level section with bottom-rule heading
|
|
87
|
+
- `<Exhibit number title source?>` — bordered card for figures, tables, side data
|
|
88
|
+
- `<FindingCard finding={f} index={i} />` — driven by `Finding` type, includes severity badge
|
|
89
|
+
- `<RecommendationCard recommendation={r} index={i} />` — driven by `Recommendation` type, includes priority badge
|
|
90
|
+
- `<Callout label?>` — left-rule emphasis block (default label "Key Takeaway")
|
|
91
|
+
- `<QuoteBlock quote attribution role? />` — pull-quote with serif quote mark
|
|
92
|
+
- `<Timeline phases={r.roadmap} />` — vertical timeline with dotted line
|
|
93
|
+
- `<SeverityBadge severity />` — pill badge: critical / high / medium / low
|
|
94
|
+
|
|
95
|
+
## Demo
|
|
102
96
|
|
|
103
97
|
```bash
|
|
104
98
|
node --experimental-strip-types ~/.pal/skills/consulting-report/tools/generate-pdf.ts ~/.pal/skills/consulting-report/demo
|
|
105
99
|
```
|
|
106
100
|
|
|
107
|
-
Inspect the
|
|
101
|
+
Renders the bundled Acme Industries example end-to-end. Inspect the resulting PDF to see the full layout before authoring your own.
|
|
108
102
|
|
|
109
103
|
## Important
|
|
110
104
|
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
105
|
+
- Run on Node ≥ 22.6 (Playwright + `--experimental-strip-types`)
|
|
106
|
+
- Bundled fonts come from Google Fonts via `next/font/google` — no licensing surface, no CDN at runtime, glyphs embedded at build time
|
|
107
|
+
- Reports are disposable artifacts of `lib/report-data.ts` + `app/page.tsx`; commit the source, not the PDF
|
|
108
|
+
- The scaffolder runs `bun install` inside the target by default — pass `--no-install` to skip
|
|
109
|
+
- Header/footer templates render with `displayHeaderFooter: true` in Playwright; don't combine with CSS `@page` margin-box rules
|
|
@@ -0,0 +1,344 @@
|
|
|
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
|
+
/* Cover page */
|
|
270
|
+
.cover-page {
|
|
271
|
+
min-height: 100vh;
|
|
272
|
+
display: flex;
|
|
273
|
+
flex-direction: column;
|
|
274
|
+
justify-content: center;
|
|
275
|
+
padding: 4rem;
|
|
276
|
+
page-break-after: always;
|
|
277
|
+
background: linear-gradient(180deg, var(--color-background) 0%, var(--color-background-secondary) 100%);
|
|
278
|
+
}
|
|
279
|
+
.cover-classification {
|
|
280
|
+
font-family: var(--font-sans);
|
|
281
|
+
font-size: 0.875rem;
|
|
282
|
+
font-weight: 600;
|
|
283
|
+
color: var(--color-destructive);
|
|
284
|
+
text-transform: uppercase;
|
|
285
|
+
letter-spacing: 0.15em;
|
|
286
|
+
margin-bottom: 4rem;
|
|
287
|
+
}
|
|
288
|
+
.cover-title {
|
|
289
|
+
font-family: var(--font-heading);
|
|
290
|
+
font-size: 3rem;
|
|
291
|
+
font-weight: 600;
|
|
292
|
+
color: var(--color-foreground);
|
|
293
|
+
line-height: 1.2;
|
|
294
|
+
margin-bottom: 1rem;
|
|
295
|
+
letter-spacing: -0.02em;
|
|
296
|
+
}
|
|
297
|
+
.cover-subtitle {
|
|
298
|
+
font-family: var(--font-heading);
|
|
299
|
+
font-size: 1.5rem;
|
|
300
|
+
color: var(--color-muted);
|
|
301
|
+
margin-bottom: 4rem;
|
|
302
|
+
font-weight: 400;
|
|
303
|
+
}
|
|
304
|
+
.cover-meta {
|
|
305
|
+
margin-top: auto;
|
|
306
|
+
padding-top: 1.5rem;
|
|
307
|
+
border-top: 1px solid var(--color-border);
|
|
308
|
+
}
|
|
309
|
+
.cover-date {
|
|
310
|
+
font-family: var(--font-sans);
|
|
311
|
+
font-size: 1rem;
|
|
312
|
+
color: var(--color-muted);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* Print styles — what Playwright sees */
|
|
316
|
+
@media print {
|
|
317
|
+
body {
|
|
318
|
+
font-size: 11pt;
|
|
319
|
+
}
|
|
320
|
+
.report-container {
|
|
321
|
+
max-width: none;
|
|
322
|
+
padding: 0;
|
|
323
|
+
}
|
|
324
|
+
.report-section,
|
|
325
|
+
.callout,
|
|
326
|
+
.exhibit,
|
|
327
|
+
.finding-card,
|
|
328
|
+
.quote-block {
|
|
329
|
+
break-inside: avoid;
|
|
330
|
+
}
|
|
331
|
+
.cover-page {
|
|
332
|
+
page-break-after: always;
|
|
333
|
+
}
|
|
334
|
+
a {
|
|
335
|
+
text-decoration: none;
|
|
336
|
+
color: var(--color-foreground);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* Page setup for PDF */
|
|
341
|
+
@page {
|
|
342
|
+
size: A4;
|
|
343
|
+
margin: 18mm 16mm;
|
|
344
|
+
}
|
|
@@ -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
|
+
}
|