Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
572 changes: 560 additions & 12 deletions assets/css/case-studies.scss

Large diffs are not rendered by default.

348 changes: 348 additions & 0 deletions content/case-studies/cursor.md

Large diffs are not rendered by default.

49 changes: 40 additions & 9 deletions docs/case-studies.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ July 2026 once Power Digital, its last user, was rebuilt on immersive.)
| Layout | Template | Used by | Body class | Style prefix |
| ------------- | ------------------------------------- | ------------------------------------------------------- | ----------------------------------- | ----------------------- |
| **Modern** | `layouts/case-studies/single.html` | Default for any case study without a `layout:` override | `case-study-modern` | `.case-study-modern` |
| **Immersive** | `layouts/case-studies/immersive.html` | MarketSpark, Power Digital (opt-in via `layout: immersive`) | `case-study-modern case-study-immersive` | `.case-study-immersive` |
| **Immersive** | `layouts/case-studies/immersive.html` | MarketSpark, Power Digital, Cursor (opt-in via `layout: immersive`) | `case-study-modern case-study-immersive` | `.case-study-immersive` |

Routing is via Hugo's `layout:` front matter param. A case study that does
**not** specify `layout:` uses `single.html` (the modern layout). MarketSpark
and Power Digital opt into immersive with `layout: immersive`.
**not** specify `layout:` uses `single.html` (the modern layout). MarketSpark,
Power Digital, and Cursor opt into immersive with `layout: immersive`.

The **modern**
layout was designed from scratch and is the default for new case studies. The
Expand Down Expand Up @@ -153,12 +153,38 @@ All shortcodes are in `layouts/shortcodes/cs-*.html` and styled under
- **New shortcode files in `layouts/shortcodes/` are not picked up by a
running `hugo serve`.** Restart serve after creating a new shortcode
template. Modifying an existing one hot-reloads fine.
- **`<ul>`-based shortcodes inside `.csi-prose` must join the prose list-rule
`:not()` chains** (`ul:not(.csi-list):not(…)`) or they inherit gradient
bullets — and must then reset `list-style`/margins themselves.
- **`<ul>`/`<ol>`-based shortcodes inside `.csi-prose` must join the prose
list-rule `:not()` chains** (`ul:not(.csi-list):not(.csi-compare__rows):not(.csi-scale)`
and `ol:not(.csi-phases):not(.csi-editor__lines)`) or they inherit gradient
bullets / numbered circles — and must then reset `list-style`/margins
themselves. The chains exist in TWO places: the base `.csi-prose` rules AND
the dark-face override (`&--pine … ul > li::before` / `ol > li::before`) —
the mint counter chips painting over `csi-editor` line numbers came from
missing the second one.
- **Article-card sub-heads are `### h3`s in prose** (used with
`csi-section article="true"`): styled with a small gradient dash above, and
optionally led by `<span class='csi-prose-kicker'>Phase 01 &middot; November
2025</span>` for an eyebrow-style kicker inside the heading (goldmark
`unsafe: true` permits the inline HTML, incl. `csi-grad` spans). Gotcha: the
heading render hook wraps every case-study heading in an anchor link, and
the pine-face `.csi-prose a:not(.button)` colour override carries FOUR
classes — the `.csi-section .csi-prose h3 a:not(.button)` inherit rule must
keep that specificity or headings render mint on dark cards. Inline markdown
chart images (`![alt](src)`) get block + radius styling from
`.csi-prose img`, plus the mint ring frame on dark faces; both they and
`.csi-inline-figure` are in the print `break-inside: avoid` list.
- **CSS-drawn figures beat images for diagrams** (`csi-split figure="…"` →
`figures/<name>.html`: inherits fonts, recolours per face, prints crisp) —
but draw with REAL elements; print drops `::before/::after` `background-image`.
Existing figures: `power-digital/terralith` (mono → stacks decomposition) and
`cursor/terralith` (the monolith as an over-stuffed `production.tfstate`
editor window with drift-noise cells + friction tags; pine-face only).
- **`cs-pullquote` two-line attribution on the light variant** — `--light`
restates `__name` (pine) and `__role` (teal): they default to white/mint for
the dark card, and the name was invisible on the cream face before this.
- **The immersive CTA (`callout:`) may carry a `<ul>`** — `.csi-cta ul` styles
it as a centred block with left-aligned gradient-square bullets (used by
Cursor's "Terraform ceilings" closing CTA).
- **The TOC reads `.Fragments`, not `.TableOfContents`.** `.TableOfContents`
is empty when read from inside a _shortcode_ (it isn't built until after the
goldmark pass), which is the original reason the TOC moved to a layout-stage
Expand Down Expand Up @@ -202,7 +228,7 @@ mint on mint and disappears. Always keep `:not(.button)` on `.cs-article a`.

---

## Immersive layout (MarketSpark, Power Digital)
## Immersive layout (MarketSpark, Power Digital, Cursor)

Opt in with `layout: immersive`. The body element carries **both**
`case-study-modern` and `case-study-immersive`:
Expand Down Expand Up @@ -234,15 +260,20 @@ _inside_ each block. Each emits a `<section class="csi-section …">` card.

| Shortcode | Purpose | Key args |
| ----------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `csi-section` | A card (eyebrow + headline + block prose). | `eyebrow`, `title` (HTML ok), `variant`, `align`, `num`, `accent`, `id` (anchor target, e.g. for `sticky_nav`; also on `csi-split`) |
| `csi-section` | A card (eyebrow + headline + block prose). | `eyebrow`, `title` (HTML ok), `variant`, `align`, `num`, `accent`, `id` (anchor target, e.g. for `sticky_nav`; also on `csi-split`), `article` (`"true"` → merged multi-topic card: one band holds what would otherwise be several sections — the whole column is CENTRED by capping the section container to the content width (else the 1320px container leaves a big right gutter); prose text stays 760px, components/inline charts breathe at 980px; author in-card topics as `### h3` sub-heads, see authoring notes) |
| `csi-split` | Text + visual side-by-side; `flip="true"` alternates sides. | `eyebrow`, `title`, `media`, `media_alt`, `media2`/`media2_alt` (second image stacked below the first), `figure` (CSS-drawn figure partial from `figures/<name>.html` instead of an image; subfolders work — `figure="power-digital/terralith"`), `flip`, `contain`, `caption`, `variant`, `ratio` (`50-50` default / `65-35` / `75-25`, text wider; ratios auto-reverse under `flip` since flip puts the media in the first grid track via `order`) |
| `csi-steps` | Numbered process cards. Place INSIDE a `csi-section`. | inner blocks split by `---`, each `title:` / `body:` |
| `csi-impact` | Outcome cards w/ gradient icon badges. INSIDE a `csi-section`. | inner blocks split by `---`, each `icon:` / `title:` / `body:`; `cols="2"` for a slimmed 2-up grid (pairs with `csi-compare`, capped to the same 980px) |
| `csi-compare` | "Then / now" migration ledger: muted old world → bold gradient new world per metric. Owns a page's hard numbers — pair with a slimmed `csi-impact` so figures aren't stated twice. Mobile stacks each row with per-cell tags. INSIDE a `csi-section`. | `before_label`, `after_label`; inner blocks split by `---`, each `label:` / `before:` / `after:` |
| `csi-compare` | "Then / now" migration ledger: muted old world → bold gradient new world per metric. Owns a page's hard numbers — pair with a slimmed `csi-impact` so figures aren't stated twice. Mobile stacks each row with per-cell tags. INSIDE a `csi-section`. | `before_label`, `after_label`; inner blocks split by `---`, each `label:` / `before:` / `after:` / optional `delta:` (a source table's "Change" column as a gradient pill after the new value — solid dark text on a gradient chip, with its own print exception so it doesn't inherit the `__after` teal fallback) |
| `csi-timeline` | Horizontal parallel-track cutover bars (old system winding down while the new ramps up): percent-positioned bars with `fade: out` / `fade: in` and an optional dashed cutover marker. Typically right after `csi-steps`. INSIDE a `csi-section`. | `marker` (percent 0–100), `marker_label`; inner blocks split by `---`, each `label:` / `note:` / `start:` / `end:` / `fade:` |
| `csi-questions` | Takeaways row of compact numbered question cards (gradient numeral inline with the question), plus an optional `outro:` "verdict" panel (leading `**bold**` renders as a block gradient lead line) and optional `cta:` paragraph divided inside the same panel. Sections containing one auto-compact like `csi-list` ones. 3-up, stacks ≤860px. INSIDE a `csi-section`. | inner blocks split by `---`, each `question:` / `body:`; standalone blocks may carry `outro:` or `cta:` (inline markdown works) |
| `csi-list` | Compact 2-col icon rows (icon chip + bold title — inline body). Space-saving sibling of `csi-impact` for secondary enumerations (e.g. "under the hood" extras) so they don't mimic the outcome grid. INSIDE a `csi-section`. | same inner format as `csi-impact` (`icon:` / `title:` / `body:`); keep bodies to one short sentence |
| `csi-testimonial` | Editorial quote band; `image=` makes it a featured cosmic band.| `name`, `title`, `company`, `photo`, `variant`, `image`, `tldr` (`"true"` → unattributed page-top summary: no quote mark, slim band, left-aligned text bare on the band with a vertical gradient bar; `**bold**` renders in the bright gradient). With `photo`, uses the avatar-left "portrait" layout (`csi-testimonial--portrait`, see below). |
| `csi-phases` | Engagement roadmap rail: numbered stops (gradient `csi-grad` numeral + REAL-element rail segment, so both print) with a date + phase name. An overview element — each stop pairs with a full phase card below (eyebrow `Phase 0N · <date>`). 4-up, single column ≤900px. INSIDE a `csi-section` (Cursor: "What Masterpoint Did"). | inner blocks split by `---`, each `date:` / `title:` |
| `csi-scale` | Order-of-magnitude inventory strip: gradient magnitude word ("hundreds", "thousands") left, what it counts right. The qualitative sibling of `csi-compare` — compare owns exact before/after figures, this owns "how big is the estate today". Recolours per face; stacks ≤640px. INSIDE a `csi-section`. | inner blocks split by `---`, each `value:` / `label:` |
| `csi-callout` | Inset spotlight panel: tinted card + gradient left bar + optional uppercase chip. For load-bearing asides that aren't quotes (`cs-pullquote` stays reserved for quotes) — e.g. Cursor's pilot-stack spotlight. Works on both faces. INSIDE a `csi-section`. | `label` (optional chip); inner is block markdown |
| `csi-editor` | Editor-window "rules" panel: stylized chrome (dots + filename tab) + one numbered row per inner LINE (CSS-counter numbers are `::before` *content*, so they print). Deliberately a dark panel on every face — reads as a code window, not a card. Built for AI-agent-rules content (Cursor's `.cursor/rules`). INSIDE a `csi-section`. | `filename` (tab label, default `.cursor/rules`); inner = one rule per line, inline markdown works, blank lines skipped |
| `csi-figure` | Embed a CSS-drawn figure partial INLINE in a section's prose (instead of in a `csi-split` media column) — for article cards that want the figure mid-flow (Cursor's terralith). INSIDE a `csi-section`. | `name` (figure partial, subfolders work — `"cursor/terralith"`), `max` (CSS max-width, default `640px`) |

Two **modern** shortcodes are also reused inside the immersive body (they render
because the body also carries `case-study-modern`, so `.case-study-modern .cs-*`
Expand Down
30 changes: 30 additions & 0 deletions layouts/partials/case-studies/figures/cursor/terralith.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{{/* Cursor terralith figure — CSS-drawn (no image asset): the monolithic

Check failure on line 1 in layouts/partials/case-studies/figures/cursor/terralith.html

View workflow job for this annotation

GitHub Actions / Trunk Check

prettier

Incorrect formatting, autoformat by running 'trunk fmt'
production workspace as one over-stuffed state file, drawn as an editor-style
file window (a nod to the Cursor IDE) with drift noise scattered through the
resource grid and friction tags beneath. Rendered in a csi-split media slot via
`figure="cursor/terralith"`. Drawn with REAL elements (print drops
::before/::after background-image), and styled for the PINE face it lives on. */}}
<div
class="csi-fig csi-fig--cursor-terralith"
role="img"
aria-label="Diagram: one monolithic production workspace holding over 7,000 resources in a single state file, with drift scattered throughout"
>
<div class="csi-fig-cterra__window" aria-hidden="true">
<div class="csi-fig-cterra__bar">
<span class="csi-fig-cterra__dot"></span><span class="csi-fig-cterra__dot"></span><span class="csi-fig-cterra__dot"></span>
<span class="csi-fig-cterra__file">production.tfstate</span>
</div>
<div class="csi-fig-cterra__cells">
{{- range seq 96 }}<i></i>{{ end -}}
</div>
<div class="csi-fig-cterra__status">
<strong>The terralith</strong>
<span>a single monolithic production workspace &middot; over 7,000 resources &middot; one state file</span>
</div>
</div>
<div class="csi-fig-cterra__tags" aria-hidden="true">
<span class="csi-fig-cterra__tag">constant drift</span>
<span class="csi-fig-cterra__tag">spikes of 10+ minutes</span>
<span class="csi-fig-cterra__tag">120 ECS service changes</span>
</div>
</div>
13 changes: 13 additions & 0 deletions layouts/shortcodes/csi-callout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{/*
csi-callout — inset spotlight panel inside a section's prose: tinted card
with a gradient left bar and an optional small uppercase chip. For a
load-bearing aside that isn't a quote (e.g. a pilot-project spotlight) —
cs-pullquote stays reserved for actual quotes. Place INSIDE a csi-section.
Args:
label optional chip text above the body (e.g. "Pilot")
Inner: block markdown.
*/}}
<aside class="csi-callout">
{{- with .Get "label" -}}<span class="csi-callout__chip">{{ . | safeHTML }}</span>{{- end -}}
<div class="csi-callout__body">{{ .Inner | $.Page.RenderString (dict "display" "block") }}</div>
</aside>
7 changes: 6 additions & 1 deletion layouts/shortcodes/csi-compare.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
label: Metric name
before: Old-world value
after: New-world value (inline markdown works in all three)
delta: +157% (optional — gradient pill after the new value,
for the improvement figure / "Change" column)
*/}}
{{- $beforeLabel := .Get "before_label" | default "Before" -}}
{{- $afterLabel := .Get "after_label" | default "After" -}}
Expand All @@ -31,6 +33,7 @@
{{- $label := "" -}}
{{- $before := "" -}}
{{- $after := "" -}}
{{- $delta := "" -}}
{{- range $lines -}}
{{- $line := trim . " \t" -}}
{{- if hasPrefix $line "label:" -}}
Expand All @@ -39,13 +42,15 @@
{{- $before = trim (strings.TrimPrefix "before:" $line) " " -}}
{{- else if hasPrefix $line "after:" -}}
{{- $after = trim (strings.TrimPrefix "after:" $line) " " -}}
{{- else if hasPrefix $line "delta:" -}}
{{- $delta = trim (strings.TrimPrefix "delta:" $line) " " -}}
{{- end -}}
{{- end -}}
<li class="csi-compare__row">
<div class="csi-compare__metric">{{ $label | $.Page.RenderString (dict "display" "inline") }}</div>
<div class="csi-compare__before"><span class="csi-compare__tag">{{ $beforeLabel | safeHTML }}</span>{{ $before | $.Page.RenderString (dict "display" "inline") }}</div>
<div class="csi-compare__arrow" aria-hidden="true"><span class="csi-grad">&rarr;</span></div>
<div class="csi-compare__after"><span class="csi-compare__tag">{{ $afterLabel | safeHTML }}</span>{{ $after | $.Page.RenderString (dict "display" "inline") }}</div>
<div class="csi-compare__after"><span class="csi-compare__tag">{{ $afterLabel | safeHTML }}</span>{{ $after | $.Page.RenderString (dict "display" "inline") }}{{ if $delta }} <span class="csi-compare__delta">{{ $delta | $.Page.RenderString (dict "display" "inline") }}</span>{{ end }}</div>
</li>
{{- end -}}
{{- end -}}
Expand Down
27 changes: 27 additions & 0 deletions layouts/shortcodes/csi-editor.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{{/*
csi-editor — an editor-window panel that renders each inner line as a
numbered "rule" row: stylized window chrome (dots + filename tab), CSS
counter line numbers, dark panel on ANY card face. Built for AI-agent-rules
content where the rules ARE the deliverable — the text sits inside the same
kind of window the engineers see them in. Place INSIDE a csi-section.
Args:
filename tab label (default ".cursor/rules")
Inner: ONE rule per line — inline markdown works; blank lines are skipped.
*/}}
{{- $filename := .Get "filename" | default ".cursor/rules" -}}
{{- $raw := .Inner -}}
{{- $lines := split $raw "\n" -}}
<div class="csi-editor" data-aos="fade-up" data-aos-duration="600" role="figure" aria-label="AI agent rules embedded in the codebase">
<div class="csi-editor__chrome" aria-hidden="true">
<span class="csi-editor__light"></span><span class="csi-editor__light"></span><span class="csi-editor__light"></span>
<span class="csi-editor__tab">{{ $filename }}</span>
</div>
<ol class="csi-editor__lines">
{{- range $lines -}}
{{- $line := trim . " \t" -}}
{{- if $line -}}
<li class="csi-editor__line">{{ $line | $.Page.RenderString (dict "display" "inline") }}</li>
{{- end -}}
{{- end -}}
</ol>
</div>
14 changes: 14 additions & 0 deletions layouts/shortcodes/csi-figure.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{{/*
csi-figure — embed a CSS-drawn figure partial
(layouts/partials/case-studies/figures/<name>.html) INLINE in a
csi-section's prose, instead of in a csi-split media column. For merged
article-style cards that want the figure mid-flow.
Args:
name figure partial name (subfolders work — e.g. "cursor/terralith")
max optional CSS max-width for the figure (default 640px)
*/}}
{{- $name := .Get "name" -}}
{{- $max := .Get "max" | default "640px" -}}
<div class="csi-inline-figure" style="--csi-fig-max: {{ $max }}" data-aos="fade-up" data-aos-duration="600">
{{ partial (printf "case-studies/figures/%s.html" $name) . }}
</div>
41 changes: 41 additions & 0 deletions layouts/shortcodes/csi-phases.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{{/*
csi-phases — engagement roadmap rail: the month-by-month arc of an
engagement rendered as numbered stops along a gradient rail. Place INSIDE a
csi-section (typically the "What Masterpoint Did" intro card) as an overview;
each stop pairs with a full phase card further down the page. 4-up on
desktop, stacks to a single column on mobile. The rail segments are REAL
elements (not pseudo backgrounds) so they survive print.
Inner: blocks separated by `---`, each block:
date: November 2025 (small uppercase label above the name)
title: Audit and High Leverage Fixes
*/}}
{{- $raw := .Inner -}}
{{- $blocks := split $raw "---" -}}
{{- $n := 0 -}}
<ol class="csi-phases" data-aos="fade-up" data-aos-duration="600">
{{- range $blocks -}}
{{- $block := trim . " \t\n" -}}
{{- if $block -}}
{{- $n = add $n 1 -}}
{{- $lines := split $block "\n" -}}
{{- $date := "" -}}
{{- $title := "" -}}
{{- range $lines -}}
{{- $line := trim . " \t" -}}
{{- if hasPrefix $line "date:" -}}
{{- $date = trim (strings.TrimPrefix "date:" $line) " " -}}
{{- else if hasPrefix $line "title:" -}}
{{- $title = trim (strings.TrimPrefix "title:" $line) " " -}}
{{- end -}}
{{- end -}}
<li class="csi-phase">
<span class="csi-phase__marker" aria-hidden="true">
<span class="csi-phase__num csi-grad">{{ printf "%02d" $n }}</span>
<i class="csi-phase__line"></i>
</span>
{{- with $date -}}<span class="csi-phase__date">{{ . | safeHTML }}</span>{{- end -}}
<strong class="csi-phase__name">{{ $title | safeHTML }}</strong>
</li>
{{- end -}}
{{- end -}}
</ol>
Loading
Loading