diff --git a/assets/css/case-studies.scss b/assets/css/case-studies.scss index 31b64f27..c21178d6 100644 --- a/assets/css/case-studies.scss +++ b/assets/css/case-studies.scss @@ -1534,6 +1534,13 @@ html:has(.cs-sticky-nav) { strong { color: #fff; } + h3, + h4 { + color: #fff; + } + h3 { + border-top-color: rgba(255, 255, 255, 0.14); + } ul > li::before { @include csi-grad-solid; } @@ -1580,12 +1587,28 @@ html:has(.cs-sticky-nav) { } } + // ---- Default LEFT alignment — the content renders as a CENTERED article + // column (text stays left-aligned, the column centres in the band) so + // wide viewports don't leave a dead right half. Only csi-section + // emits --left (csi-split carries its own grid and no align class), + // and every MarketSpark / Power Digital section is --center, so this + // only shapes the consolidated narrative bands (Cursor). + &--left { + .csi-section__head, + .csi-prose { + max-width: 860px; + margin-left: auto; + margin-right: auto; + } + } + // ---- COMPACT band — applied automatically to any section containing a // csi-list or csi-questions (no opt-in arg): same content, less // vertical real estate. Tighter padding, smaller head, denser prose - // and pull quote. - &:has(.csi-list), - &:has(.csi-questions) { + // and pull quote. `roomy="true"` (csi-section--roomy) opts a long + // consolidated band back out — its bulk is narrative, not the list. + &:has(.csi-list):not(.csi-section--roomy), + &:has(.csi-questions):not(.csi-section--roomy) { padding: 2.5rem 0; @media (max-width: 991px) { padding: 2.1rem 0; @@ -1821,6 +1844,46 @@ html:has(.cs-sticky-nav) { } } + // In-card subsection heads + inline media — consolidated bands (Cursor) + // carry several source sections inside ONE band: markdown ### become the + // in-card chapter heads (hairline divider between chapters), markdown + // images the inline charts, and csi-figure drops a CSS-drawn figure into + // the prose flow. Pine faces recolour the heads inside the --pine block. + .csi-prose h3, + .csi-prose h4 { + color: $csi-ink; + font-weight: 800; + line-height: 1.3; + } + .csi-prose h3 { + font-size: clamp(1.35rem, 2.2vw, 1.7rem); + margin: 2.8rem 0 1rem; + padding-top: 2.4rem; + border-top: 1px solid $csi-line; + } + .csi-prose > h3:first-child { + margin-top: 0.4rem; + padding-top: 0; + border-top: 0; + } + .csi-prose h4 { + font-size: 1.18rem; + margin: 2.2rem 0 0.8rem; + } + .csi-prose img { + display: block; + width: 100%; + height: auto; + border-radius: 14px; + margin: 1.7rem auto; + box-shadow: 0 18px 44px -28px rgba(8, 30, 30, 0.45); + } + .csi-figure { + display: flex; + justify-content: center; + margin: 2.2rem auto; + } + // --------------------------------------------------------------------------- // SPLIT — text + visual side by side, alternating with --flip // --------------------------------------------------------------------------- @@ -2294,6 +2357,81 @@ html:has(.cs-sticky-nav) { // NOTE: the ledger stays a WHITE card on dark faces too (no pine override) // — the hard numbers read best on white, and the card pops off the band. + // --------------------------------------------------------------------------- + // TABLE — csi-table: a compact metric table for numbers that outgrow the + // two-column csi-compare ledger (an extra "Change"/"Improvement" delta + // column). Same rule as the ledger: it stays a WHITE card on dark faces. + // The delta column (`accent="true"`) carries the brand gradient — its + // classes are registered in the print gradient-fallback list. + // --------------------------------------------------------------------------- + .csi-table { + max-width: 980px; + margin: 2.2rem auto 0; + text-align: left; + + &__card { + background: #fff; + border: 1px solid $csi-line; + border-radius: 16px; + box-shadow: 0 16px 36px -26px rgba(8, 30, 30, 0.32); + overflow: hidden; + } + &__title { + padding: 1rem 1.4rem 0.9rem; + font-size: 1.02rem; + font-weight: 800; + color: $csi-ink; + border-bottom: 1px solid $csi-line; + } + &__scroll { + overflow-x: auto; + } + table { + width: 100%; + min-width: 620px; + border-collapse: collapse; + margin: 0; + font-size: 0.95rem; + } + thead { + // strip tint lives on the row, NOT the th — a bg on the th would + // out-specify the gradient mixin on th.csi-table__delta-head and leave + // its clipped text invisible (flat bg + transparent fill) + background: rgba(20, 48, 47, 0.03); + } + th { + padding: 0.8rem 1.4rem; + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.14em; + text-transform: uppercase; + text-align: left; + color: #8aa09f; + border-bottom: 1px solid $csi-line; + white-space: nowrap; + } + td { + padding: 0.95rem 1.4rem; + color: #7d9291; + line-height: 1.45; + vertical-align: top; + } + td:first-child { + font-weight: 700; + color: $csi-ink; + } + tbody tr + tr td { + border-top: 1px solid $csi-line; + } + &__delta-head { + @include csi-grad-light; + } + &__delta { + font-weight: 800; + @include csi-grad-light; + } + } + // --------------------------------------------------------------------------- // FIGURE — TERRALITH (csi-split figure="terralith"). CSS-drawn monolith → // isolated stacks: no image asset, inherits page fonts, recolours per face, @@ -3075,6 +3213,8 @@ html:has(.cs-sticky-nav) { .csi-questions__outro-main strong, .csi-compare__col--after, .csi-compare__after, + .csi-table__delta-head, + .csi-table__delta, .cs-hero__title .text-gradient { background: none !important; -webkit-background-clip: border-box !important; @@ -3117,6 +3257,8 @@ html:has(.cs-sticky-nav) { .csi-testimonial, .csi-impact__card, .csi-compare__row, + .csi-table__card, + .csi-prose img, .csi-fig-terralith__mono, .csi-fig-terralith__stack, .csi-timeline, diff --git a/content/case-studies/cursor.md b/content/case-studies/cursor.md new file mode 100644 index 00000000..0f5f2482 --- /dev/null +++ b/content/case-studies/cursor.md @@ -0,0 +1,325 @@ +--- +title: "75% Faster Plans, 2x Daily Applies: How Cursor Rebuilt Its Terraform Workflow with Masterpoint" +weight: 1 +description: "At Masterpoint, we focused on Terraform and IaC improvements so our client Cursor could focus on making their users extraordinarily productive." + +layout: immersive + +# Hero +eyebrow: "CASE STUDY SUCCESS STORY" +client: "Cursor" +client_logo: /img/case-studies/cursor/cursor-lockup-white.svg +client_logo_height: 30px +hero_title: "75% Faster Plans, 2x Daily Applies: How Cursor Rebuilt Its Terraform Workflow with Masterpoint" +hero_aside_image: /img/case-studies/cursor/deploy-frequency-growth.png +hero_aside_alt: "Line chart of Cursor's weekly infrastructure PRs climbing from 194 in October 2025 to 500+ by May 2026" + +# At-a-glance stat strip (appears under hero) +stat_bar: + - value: "~8 min → ~2 min" + label: "median plan time (production)" + - value: "~194 → 500+" + label: "deployment PRs / week (+157%)" + - value: "54 → 5" + label: "median resources affected (91% smaller blast radius)" + - value: "100+" + label: "narrow, independent root modules" + +# Preview / OG +preview_image: /img/case-studies/cursor/plan-time-before-after.png +og_img: /img/case-studies/cursor/plan-time-before-after.png + +sitemap: + priority: 0 + +callout: >- +

If your team is hitting Terraform + ceilings like:

+ +

Masterpoint can help. We've done this work at scale, for engineering + organizations that can't afford to slow down. + Get in touch →

+--- + +{{< csi-testimonial tldr="true" variant="pine" >}} +At Masterpoint, we focused on **Terraform and IaC improvements** so our client Cursor could focus on making their users **extraordinarily productive**. +{{< /csi-testimonial >}} + +{{< csi-section eyebrow="At a Glance" title="Key Value Delivered" variant="light" align="center" >}} +{{< csi-impact cols="2" >}} +icon: fa-gauge-high +title: Dramatically faster plans +body: Plan time dropped significantly. Originally plan time had spikes of 10+ minutes; after our work, plan time was roughly 2 minutes. +--- +icon: fa-rocket +title: Improved delivery speed +body: Engineers shipped 157% more PRs per week after the engagement, and have increased shipping cadence since. +--- +icon: fa-rotate +title: More frequent, safer deploys +body: Weekly infrastructure PRs grew from 194 to 500+, and fewer plans were abandoned as the apply-to-plan ratio improved from 55% to 64%. +--- +icon: fa-crosshairs +title: Safer, more contained changes +body: The median Terraform run now touches 5 resources instead of 54 — a 91% smaller blast radius, so engineers can reason about exactly what a change will affect before applying it. +{{< /csi-impact >}} +{{< /csi-section >}} + +{{< csi-section id="the-challenge" eyebrow="The Challenge" title="The Problem: Infrastructure That Couldn’t Keep Up" variant="pine" >}} +By late 2025, Cursor's Terraform setup had become one of the biggest friction points in the engineering org. The company behind the Cursor IDE experienced issues familiar to fast-growing startups: infrastructure code written quickly, then scaled by copy-pasta rather than through careful design. + +Those early Terraform patterns were replicated at machine speed as engineers dogfooded Cursor. They were copied and instantiated hundreds of times without the architectural guardrails needed to keep the system manageable. The result was a classic [**terralith**](/blog/terralith-monolithic-terraform-architecture/): critical infrastructure was managed as a single monolithic production workspace with over 7,000 resources in one state file. + +{{< csi-figure name="cursor/terralith" >}} + +And because that workspace was so large, it was too slow. + +{{< cs-pullquote name="Travis McPeak" title="Security Lead" company="Cursor" >}} +We had one giant workspace that took way too long to plan. It was killing us. +{{< /cs-pullquote >}} + +But the problem wasn't just the lack of speed. It was a lack of engineering confidence in the IaC system. Issues included: + +- The AWS console was used to make changes during incidents and these fixes were never rolled into TF, and so were later inadvertently reverted. +- Managing ECS deployments through Terraform caused constant drift in the production terralith, flooding PR diffs with hundreds of unrelated changes. + +Over time, the team stopped trusting the system, let alone reading the diffs. + +{{< cs-pullquote name="Travis McPeak" title="Security Lead" company="Cursor" >}} +We would have engineers click apply on prod workspaces that had 120 ECS service changes. The ECS services would change all the time because of drift, so the team became desensitized to large Terraform plan changes. +{{< /cs-pullquote >}} + +This confusion caused downtime, such as a network firewall change that caused a 10 minute outage. + +The existing platform, Terraform Cloud, introduced some friction too: + +- Workspace management relied on manual [ClickOps](/blog/terraform-opentofu-terminology-breakdown/#clickops) configuration for each new workspace. +- The SSO experience was inconsistent. +- TFC's resource-under-management (RUM) pricing model made it daunting to scale. +{{< /csi-section >}} + +{{< csi-section id="the-work" eyebrow="The Engagement" title="What Masterpoint Did" variant="light" roomy="true" >}} +Masterpoint came in for a systematic overhaul, working across the architecture, the platform, and the developer workflow. + +{{< csi-timeline marker="50" marker_label="February 2026 · migration done" >}} +label: Terraform Cloud + Terraform +note: workspaces migrate off in phases +start: 0 +end: 50 +fade: out +--- +label: Spacelift + OpenTofu +note: pilot stack first, then bulk migration +start: 22 +end: 100 +fade: in +{{< /csi-timeline >}} + +### November 2025 - Audit and High Leverage Fixes + +The engagement opened with an [audit](/services/audit/) of Cursor's infrastructure: + +- cloud environments +- IaC setup +- CI/CD pipelines +- networking +- observability + +Within a week, Masterpoint delivered an audit report, a best practices checklist, and a security scan. The findings included IAM duplication accounted for 70% of the production TF state and noisy plan diffs caused engineer fatigue. + +The audit phase targeted the highest-leverage fixes first: IAM resource deduplication, workspace trigger and path filter corrections so plans only ran when relevant code changed, and initial provider alias splits to begin isolating staging and ML infrastructure from the main production workspace. + +By November 25th, the IAM deduplication fixes had landed. + +{{< csi-table head="Metric | Before | After | Change" accent="true" >}} +row: Resource Count | 8,480 | 4,593 | 45.8% reduction (3,887 resources removed) +{{< /csi-table >}} + +The number of production resources dropped from 8,480 to 4,593, a 46% reduction. Mean plan time fell from 8.1 minutes to 4.9 minutes, a 39% improvement. The P95 plan time improved even more. + +{{< csi-table head="Metric | Before (Nov 20–24) | After (Nov 25–Dec 8) | Improvement" accent="true" >}} +row: Mean | 488.8s (8.1 min) | 296.6s (4.9 min) | 39.3% faster +row: P50 (Median) | 423s (7.1 min) | 272s (4.5 min) | 35.7% faster +row: P95 | 749s (12.5 min) | 414s (6.9 min) | 44.7% faster +row: Sample Size | 38 runs | 184 runs | n/a +{{< /csi-table >}} + +This was accomplished without touching the workspace structure, but the systems could still be improved. + +### December 2025 - Spacelift Migration Begins + +With the first phase complete, Masterpoint turned to the platform. + +As mentioned above, TFC's ClickOps workspace management, SSO troubles, and resource-under-management pricing were all inhibiting Cursor's velocity and growth. + +Masterpoint recommended a move to [Spacelift](/services/spacelift/) to address these issues. + +The team kicked off the Spacelift migration in mid-December, starting with the groundwork: + +- naming and tagging standards +- AWS multi-account IAM role architecture +- Okta SSO configuration + +Rather than migrating everything at once, a low-risk pilot Spacelift stack (the equivalent of a TFC workspace) was used to validate the approach. The end-to-end fully automated pilot project was completed within days. + +Masterpoint also began evaluating a full migration from the Terraform runtime to [OpenTofu](https://opentofu.org/). + +### January to February 2026 - Bulk Migration + +With a plan covering a migration of dozens of TFC workspaces across four phases, the team worked systematically. By late January, the bulk of workspaces had been migrated to Spacelift stacks with no operational downtime for the Cursor engineering organization. + +By February, the migration was done. All workspaces had been moved from TFC to Spacelift and the runtime was converted from Terraform to OpenTofu. Migrating to OpenTofu freed Cursor from licensing constraints and unlocked capabilities unavailable or paywalled in Terraform, such as state encryption, OpenTelemetry support, and provider iteration. + +As part of the final migration work, the production workspace was reduced by splitting out various provider-aliased resources into dedicated stacks, continuing the decomposition work that had started with IAM deduplication in November. + +### February to May 2026 - AI Agent Guardrails And Additional Improvements + +After the migration was completed, Masterpoint continued to improved system usability and speed by: + +- restructuring Route53 DNS API requests into optimized, dedicated TF modules to address AWS's strict 5 requests/second limit +- continuing to break up large state files into narrower root modules, allowing for faster plans (further reducing critical infrastructure plans from roughly 5 minutes to under 2 minutes) and decreased blast radius +- implementing child module versioning with OCI registries to enable staged rollouts and safer change control of critical TF resources +- resolving the ECS drift issue by using the ["Task Definition Template Pattern"](https://newsletter.masterpoint.io/p/deploying-your-apps-into-ecs) +- implementing OpenTofu's [OTel tracing](https://opentofu.org/docs/internals/tracing/) to set up the Cursor team for longterm visibility into their IaC throughput + +Because Cursor's engineering team uses Cursor to write Terraform, Masterpoint embedded architectural knowledge directly into the codebase as AI agent skills and rules. + +This AI context encodes the decisions that AI models cannot anticipate on their own: + +{{< csi-list >}} +icon: fa-sitemap +title: when to create a new root module vs. extend an existing one +--- +icon: fa-crosshairs +title: how to scope blast radius in a root module +--- +icon: fa-cubes +title: how to break up a high blast radius monolithic root module +--- +icon: fa-database +title: data source patterns and optimizations +--- +icon: fa-box-open +title: how to use OCI-sourced child modules +--- +icon: fa-shield-halved +title: security practices, such as TF module SHA pinning +--- +icon: fa-magnifying-glass-chart +title: debugging performance bottlenecks, through methods such as logging and OpenTelemetry traces +{{< /csi-list >}} + +{{< cs-pullquote name="Ravi Rahman" title="Software Engineer" company="Cursor" >}} +The AI agent skills and rules help us build our IaC correctly while at the same time moving faster. +{{< /cs-pullquote >}} + +Every IaC optimization Masterpoint delivered was paired with instructions to prevent anti-patterns from being reintroduced. +{{< /csi-section >}} + +{{< csi-section id="the-results" accent="true" title="The Results" variant="pine" roomy="true" >}} +### What Cursor’s Infrastructure as Code Manages + +The Cursor infrastructure today includes: + +{{< csi-list >}} +icon: fa-server +title: hundreds of ECS services with tens of thousands of live running tasks +--- +icon: fa-microchip +title: thousands of EC2 instances +--- +icon: fa-layer-group +title: dozens of Redis clusters +--- +icon: fa-database +title: a few dozen databases +--- +icon: fa-network-wired +title: in the triple digits of ALB and NLB load balancers +{{< /csi-list >}} + +In all 40k+ Terraform resources are managed using the new platform. All of this exists in 100+ narrow, independent root modules, with [OpenTofu](https://opentofu.org/), on [Spacelift](https://spacelift.io/), using Okta SSO, stored in S3-backed state, and paired with AI agent rules to keep the architecture clean as the codebase grows. + +### Numbers That Matter + +{{< csi-compare >}} +label: Median plan time (production) +before: ~8 min +after: ~2 min +--- +label: Deployment PRs / week +before: ~194 +after: 500+ (+157%) +--- +label: Median resources affected +before: 54 +after: 5 (91% reduction in blast radius) +{{< /csi-compare >}} + +### Plan time went from 10+ minutes to 2 minutes + +![Dial gauge: a typical Terraform run dropped from 10+ minutes to a steady 2 minutes per run, 80% of the wait gone](/img/case-studies/cursor/plan-time-dial.png) + +The production workspace plan time dropped from over 10+ minutes, with spikes to 15 minutes, during the November baseline to around 2 minutes by mid 2026. + +This is an improvement of roughly 75% from where the engagement started, and more than 80% from the pre-engagement peak. + +### Deployment frequency increased + +![Stat card: 2.6x more PRs shipped, from 194 to 500+ changes shipped every week, a 157% jump in deployment frequency](/img/case-studies/cursor/deploy-frequency-stat.png) + +Weekly merged PRs against infrastructure as code went from approximately 194 per week to over 500 a week, a 157% increase. They've only accelerated since then. + +### The productivity lift was significant + +This gain wasn't from growing the engineering organization: a controlled same-cohort analysis of 34 engineers active both before and after the Masterpoint engagement found that PR throughput grew by 121%. + +{{< csi-table head="Metric | Pre-engagement | End of Engagement | Change" accent="true" >}} +row: Cohort throughput | 63 PRs/wk | 140 PRs/wk | +121% +row: Per-active-week cadence | 5.37 PRs/author/wk | 9.19 PRs/author/wk | +71% +{{< /csi-table >}} + +### Infrastructure became democratized + +![Bar chart of Terraform root module workspaces growing from 21 in October 2025 to 141 by May 2026, a 571% increase](/img/case-studies/cursor/workspaces-democratized.png) + +Looking beyond the controlled same-cohort set of engineers, 89% of all engineers who work with infrastructure now ship Terraform PRs each month. + +Previously, workspace creation required manual ClickOps and admin access. Today, engineers create and manage their own stacks. The number of separately managed modules went from 26 to 100+. Engineers are now creating their own product/domain scoped infrastructure instead of jumbling it in the primary monolithic workspace. +{{< /csi-section >}} + +{{< csi-section accent="true" title="What Changed for the Team" variant="light" >}} +### Engineers know what an IaC Terraform change will touch + +Before the engagement, engineers couldn't predict what a change would actually touch in a workspace. A database might live in a workspace named for networking infrastructure, so even finding where something was managed required tribal knowledge. Dependencies between workspaces were just as opaque: touching one piece of infrastructure could ripple into systems that had nothing to do with the change — or surface long-standing drift that did. Plans intended for one system would silently queue in unrelated workspaces, waiting for the next person to find them with no context on where they came from or whether they were safe to apply. + +{{< cs-pullquote name="Ravi Rahman" title="Software Engineer" company="Cursor" >}} +The Terraform experience has gotten way better. For the first time since I've been here, we just merge and apply. If something's wrong, the system lets us know. It's fast and moves at the speed we need. +{{< /cs-pullquote >}} + +With smaller, focused stacks, plans are scoped to the change at hand. The median Terraform run now touches just 5 resources instead of 54 — a 91% smaller blast radius — so engineers know precisely what a plan will affect before they run it. + +![Before and after dot graph: a median Terraform run once touched 54 resources, now it averages just 5, a 91% reduction in blast radius](/img/case-studies/cursor/blast-radius.png) + +### Architectural flexibility enables the business and product delivery + +Before the engagement, the terralith made architectural changes expensive and risky. Touching one piece of infrastructure meant disrupting unconnected components. + +With narrow stacks owning a focused piece of infrastructure, the Cursor engineering team can more easily modify their architecture. + +New services, providers, and cloud targets can be added as their own stacks, which is a foundation that directly enables expansion into multi-cloud infrastructure as Cursor continues to scale. + +### Cursor’s agents are set up for future success + +Since Cursor's engineers rely on Cursor to write their own Terraform, Masterpoint embedded the new architecture into AI agent rules and skills that sit directly within the codebase. + +These rules capture the patterns and decisions that LLMs can't figure out on their own, setting the team up to consistently ship high-quality infrastructure code, now and as the system evolves. +{{< /csi-section >}} + +{{< csi-testimonial name="Travis McPeak" title="Security Lead" company="Cursor" variant="pine" image="/img/bg_our_word.jpg" >}} +Masterpoint didn't just fix our IaC. They gave our entire engineering org the confidence to move fast. We went from a system people were afraid to touch Terraform to where nearly every engineer is quickly and assuredly shipping infrastructure changes every week. +{{< /csi-testimonial >}} diff --git a/docs/case-studies.md b/docs/case-studies.md index 54402cc6..28113eb8 100644 --- a/docs/case-studies.md +++ b/docs/case-studies.md @@ -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 @@ -159,6 +159,34 @@ All shortcodes are in `layouts/shortcodes/cs-*.html` and styled under - **CSS-drawn figures beat images for diagrams** (`csi-split figure="…"` → `figures/.html`: inherits fonts, recolours per face, prints crisp) — but draw with REAL elements; print drops `::before/::after` `background-image`. + The `cursor/terralith` figure reuses the shared `.csi-fig-terralith__*` + classes from the Power Digital figure with different chips/labels — new + terralith-story figures need zero new CSS if they keep that structure. +- **Consolidated ("mega") bands — the Cursor page pattern.** Cursor's body is + just 7 bands; several source sections live INSIDE one band. The pieces that + make that work: markdown `###` subheads inside a section render as in-card + chapter heads (`.csi-prose h3` — hairline top divider between chapters, + link-colored via the heading render hook; `h4` smaller, no divider; both + recolour on pine); markdown images render as rounded, shadowed cards + (`.csi-prose img`, full column width, in the print `break-inside` list); + `csi-figure` drops a CSS figure inline; and `roomy="true"` keeps the band + full-sized despite containing a `csi-list`. Default-left sections render the + head + prose as a **centered 860px article column** (text left-aligned, the + column centred in the band — `.csi-section--left` rule) so wide viewports + don't get a dead right half; only `csi-section` emits `--left`, and all + MarketSpark/Power Digital sections are `--center`, so the rule is + Cursor-only in practice. Alternation still holds — the page is pine/light + alternating with the cosmic testimonial + CTA closing. +- **Match image backgrounds to card faces.** The Cursor graphics exist in + white-background and pine-background variants. Inline in prose, dark charts + blend into `pine` bands and white stat cards pop like the ledger's white + card; on `light` bands the reverse. When only one variant exists, pick the + band variant to match the image, not vice versa. +- **A dark data graphic can be the hero background.** Cursor sets + `hero_aside_image` to the dark deploy-frequency line chart — the hero scrim + keeps the title legible on the left while the rising line + "500+" label + show through on the right. Works because the chart is pine-toned; a busy or + light image would fight the frosted title box. - **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 @@ -234,11 +262,13 @@ _inside_ each block. Each emits a `
` 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`), `roomy` (`"true"` → opt out of the automatic `csi-list` compact treatment — for long consolidated bands whose bulk is narrative) | | `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/.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-table` | Compact metric table for numbers that outgrow the two-column ledger (an extra "Change"/"Improvement" delta column — Cursor's plan-time and cohort tables). Like `csi-compare` it stays a WHITE card on dark faces. Header-strip tint lives on `thead`, NOT `th` (a `th` background out-specifies the gradient mixin on the delta header and renders it invisible). `min-width: 620px` + `overflow-x: auto` wrapper → horizontal scroll on phones. INSIDE a `csi-section`. | `head` (pipe-separated column headers), `title` (optional heading inside the card, above the table), `accent` (`"true"` → last column bold gradient, `.csi-table__delta`); inner is one `row:` line per row, cells pipe-separated, inline markdown OK | +| `csi-figure` | Drops a CSS-drawn figure partial INLINE in a section's prose flow (vs `csi-split figure=` which puts it in the media column). Used by Cursor's consolidated challenge band to place the terralith mid-narrative. | `name` (path under `layouts/partials/case-studies/figures/`, no extension) | | `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 | @@ -417,7 +447,8 @@ Case studies print (Ctrl/Cmd+P → Save as PDF) styled to match the screen. transparent fill does not render reliably in Chrome's PDF path: the run stops wrapping and gets sliced by the hero card's `overflow:hidden`, or the gradient paints as a solid rectangle. So `.text-gradient` / `.csi-grad` / - `.csi-step__num` / `.csi-testimonial__mark` / `.cs-hero__lockup-x` fall + `.csi-step__num` / `.csi-testimonial__mark` / `.cs-hero__lockup-x` / + `.csi-table__delta` / `.csi-table__delta-head` fall back to a solid brand colour (`#2ad9c2`) in print — any NEW class using the grad mixins directly (not via `.csi-grad`) must join this list. The on-screen multi-stop gradient becomes a single teal in the PDF — an @@ -443,6 +474,7 @@ Case studies print (Ctrl/Cmd+P → Save as PDF) styled to match the screen. 7. **Page-break control — prevents cards clipping across page boundaries.** `break-inside: avoid` on the **small, repeating** cards (`.cs-stat`, `.cs-pullquote`, `.csi-testimonial`, `.csi-impact__card`, `.csi-compare__row`, + `.csi-table__card`, `.csi-fig-terralith__mono/__stack`, `.csi-timeline`, `.csi-question`) pushes a whole card to the next page rather than slicing its top/bottom edge. **Only apply it to small cards** — putting it on large one-off blocks (the About panel, diff --git a/layouts/partials/case-studies/figures/cursor/terralith.html b/layouts/partials/case-studies/figures/cursor/terralith.html new file mode 100644 index 00000000..76896cf3 --- /dev/null +++ b/layouts/partials/case-studies/figures/cursor/terralith.html @@ -0,0 +1,58 @@ +{{/* Cursor terralith figure — CSS-drawn (no image asset): the monolithic +production workspace (7,000+ resources in one state file) decomposed into +narrow, independent root modules. Rendered in a csi-split media slot via +`figure="cursor/terralith"`. Reuses the shared `.csi-fig-terralith__*` styles +(see the Power Digital figure) so it inherits fonts, recolours per card face, +and stays crisp in print. */}} + diff --git a/layouts/shortcodes/csi-figure.html b/layouts/shortcodes/csi-figure.html new file mode 100644 index 00000000..f8f94ef3 --- /dev/null +++ b/layouts/shortcodes/csi-figure.html @@ -0,0 +1,11 @@ +{{/* + csi-figure — drop a CSS-drawn figure partial INLINE in a csi-section's prose + flow (csi-split's `figure=` arg puts the figure in the media column; this + puts it mid-narrative instead — used by consolidated bands). + Args: + name path under layouts/partials/case-studies/figures/, no extension — + e.g. name="cursor/terralith" +*/}} +
+ {{ partial (printf "case-studies/figures/%s.html" (.Get "name")) . }} +
diff --git a/layouts/shortcodes/csi-section.html b/layouts/shortcodes/csi-section.html index 3ff1e0c1..198bde24 100644 --- a/layouts/shortcodes/csi-section.html +++ b/layouts/shortcodes/csi-section.html @@ -8,6 +8,9 @@ align left (default) | center num optional big watermark number (e.g. "01") accent "true" → render just the gradient line accent (no eyebrow text) + roomy "true" → opt OUT of the automatic compact treatment below; use on + long consolidated bands that happen to contain a csi-list but + whose bulk is narrative prose (e.g. the Cursor mega-sections) Note: a section containing a csi-list automatically renders as a denser, compact band (via `.csi-section:has(.csi-list)` in the CSS) — tighter padding, smaller head, denser prose and pull quote. @@ -21,7 +24,8 @@ {{- $num := .Get "num" -}} {{- $accent := .Get "accent" -}} {{- $id := .Get "id" -}} - +{{- $roomy := eq (.Get "roomy") "true" -}} +
{{- if or $eyebrow $title $accent -}}
diff --git a/layouts/shortcodes/csi-table.html b/layouts/shortcodes/csi-table.html new file mode 100644 index 00000000..f0d89254 --- /dev/null +++ b/layouts/shortcodes/csi-table.html @@ -0,0 +1,47 @@ +{{/* + csi-table — a compact metric table for data that doesn't fit csi-compare's + two-column ledger shape (e.g. a Before/After PLUS a "Change"/"Improvement" + delta column). Place INSIDE a csi-section. Like csi-compare it stays a WHITE + card on dark faces — hard numbers read best on white. + Args: + head pipe-separated column headers, e.g. "Metric | Before | After | Change" + title optional heading rendered inside the card, above the table (safeHTML) + accent "true" → the LAST column renders bold in the brand gradient + (.csi-table__delta — registered in the print gradient-fallback list) + Inner: one `row:` line per table row, cells pipe-separated (inline markdown OK): + row: Mean | 488.8s (8.1 min) | 296.6s (4.9 min) | 39.3% faster +*/}} +{{- $headCells := split (.Get "head") "|" -}} +{{- $ncols := len $headCells -}} +{{- $accent := eq (.Get "accent") "true" -}} +
+
+ {{- with .Get "title" }} +
{{ . | safeHTML }}
+ {{- end }} +
+ + + + {{- range $i, $h := $headCells }} + {{ trim $h " " | safeHTML }} + {{- end }} + + + + {{- range split .Inner "\n" }} + {{- $line := trim . " \t\r" }} + {{- if hasPrefix $line "row:" }} + + {{- $cells := split (trim (strings.TrimPrefix "row:" $line) " ") "|" }} + {{- range $i, $c := $cells }} + {{ trim $c " " | $.Page.RenderString (dict "display" "inline") }} + {{- end }} + + {{- end }} + {{- end }} + +
+
+
+
diff --git a/static/img/case-studies/cursor/blast-radius.png b/static/img/case-studies/cursor/blast-radius.png new file mode 100644 index 00000000..85735aeb Binary files /dev/null and b/static/img/case-studies/cursor/blast-radius.png differ diff --git a/static/img/case-studies/cursor/cursor-lockup-white.svg b/static/img/case-studies/cursor/cursor-lockup-white.svg new file mode 100644 index 00000000..c0d4466f --- /dev/null +++ b/static/img/case-studies/cursor/cursor-lockup-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/img/case-studies/cursor/deploy-frequency-growth.png b/static/img/case-studies/cursor/deploy-frequency-growth.png new file mode 100644 index 00000000..19c8a609 Binary files /dev/null and b/static/img/case-studies/cursor/deploy-frequency-growth.png differ diff --git a/static/img/case-studies/cursor/deploy-frequency-stat.png b/static/img/case-studies/cursor/deploy-frequency-stat.png new file mode 100644 index 00000000..0f4f02e8 Binary files /dev/null and b/static/img/case-studies/cursor/deploy-frequency-stat.png differ diff --git a/static/img/case-studies/cursor/plan-time-before-after.png b/static/img/case-studies/cursor/plan-time-before-after.png new file mode 100644 index 00000000..f0221f49 Binary files /dev/null and b/static/img/case-studies/cursor/plan-time-before-after.png differ diff --git a/static/img/case-studies/cursor/plan-time-dial.png b/static/img/case-studies/cursor/plan-time-dial.png new file mode 100644 index 00000000..a6d9770d Binary files /dev/null and b/static/img/case-studies/cursor/plan-time-dial.png differ diff --git a/static/img/case-studies/cursor/workspaces-democratized.png b/static/img/case-studies/cursor/workspaces-democratized.png new file mode 100644 index 00000000..e2974a30 Binary files /dev/null and b/static/img/case-studies/cursor/workspaces-democratized.png differ