Bruno DelorenceBruno
Appearance
Appearance

Choose your reading theme.

Contact

Redline review

New

An AI contract review card: steps tick off, a market swarm places their draft and the redline, and the next redline drafts below.

redline-review/redline-review.tsx
Live · interactive

Larkspur MSA

v4 · 42 pages · Playbook SaaS vendor v3

  1. Read Larkspur_MSA_v4.pdf 42 pages · 18 clauses

  2. Checked against SaaS vendor v3 15 ok · 3 off

§ 9.2 Liability cap vs market

1 dot = 2 SaaS MSAs

  • Their draft 3 mo · p4
  • Redline 12 mo · p49
  • Below playbook

Drafting § 12.4 Indemnity 2 of 3

The live component, numbered part by part. Every measurement is read from it as it is drawn. Point at a row to outline its part, or at a part to find its row.

When to use

  • An agent reviewing a contract against a playbook, where the reader has to settle each change it proposes.
  • A number that only means something next to others like it: a cap, a notice period, a fee, placed among past deals.
  • Showing the work before the verdict: what the agent read and checked, with counts, ahead of the clause it flags.
  • A review that keeps going while the reader decides: the next redline drafts below, and they can steer it in words.

When not to use

  • The whole contract with every change marked in place. Use a document view with tracked changes.
  • Many flagged clauses to sort and assign. Use a table of clauses with filters and bulk actions.
  • A value with no market behind it, or fewer than a few dozen comparable deals: the swarm reads as noise. Show the value against the playbook alone.
  • A plain chat with the drafter. Use a chat thread; this card is built around one clause at a time.
  • Uploading the contract. Use a dropzone first; this card starts once the file is read.

You can add this component with the shadcn CLI or by hand:

Into components/redline-review of any React project, with no setup:

Terminal
npx shadcn@latest add https://brunodelorence.com/r/redline-review.json

Either way it needs React 19 and a bundler that supports CSS Modules, such as Next.js or Vite. It has a dark colour scheme: it follows your page’s color-scheme, light unless the page sets one, or its colorScheme prop, with nothing to configure. Browsers without light-dark() show it light.

Usage

contract-review.tsx
import { RedlineReview } from './redline-review';export function ContractReview() {  return (    <RedlineReview      title="Larkspur MSA"      draftMonths={3}      redlineMonths={12}      onDecision={(decision) => console.log(decision)}      onSend={(prompt, mode) => console.log(mode, prompt)}    />  );}

Each state below is a live instance, locked so you can study it.

The pieces it is built from, each with its measurements read live.

An agent reads a contract, checks it against a playbook and finds three clauses off. The liability cap shows where it stands in the market: a swarm of past contracts, their draft far down the left, the playbook minimum as a dashed line and the redline lit on it, with percentiles counted from the data. Accept or reject it, watch the next redline draft, and give the drafter your own instruction.

  • The market is one SVG of 729 dots in columns along a months axis, one dot for two contracts: grey below the playbook minimum, violet from it, each fainter the farther it stacks from the axis. It scales with the card, while the labels around it are HTML and keep their size.
  • The percentiles are worked out from the same data: the share of contracts with a lower cap, half of those with the same one. Their draft at 3 months lands at p4, the redline at 12 at p49.
  • When the chart plays, every dot falls into place on a CSS animation of its own, delayed by its column and its distance from the axis. The playbook line then draws down, the draft ring pops, the arc draws itself through a mask whose dash offset runs from 1 to 0, and the redline dot pops at its end while the percentiles count up. A hidden copy of each final number holds its place, so the legend never shifts.
  • Accept or Reject settles the redline: the losing side dims, the note says what stands, and Undo opens it again. The tab counts the redlines still waiting.
  • The drafting line moves on by itself while on screen: a violet-to-red glow under the row stretches by scaleX, and the count moves to the next clause. Cancel stops it, and Resume picks it up again.
  • While on screen and untouched, a demo plays the review from the start: the page and clause counts tick up, the chart plays, an instruction is typed, the redline is accepted and the instruction sent. Any real input hands the card to the reader.
  • Every colour is a light-dark() token, so the card follows the page or the colorScheme prop. Off screen, in a hidden tab or under reduced motion nothing moves, and the card is drawn at rest.
PropTypeDefaultDescription
titlestring'Larkspur MSA'The contract’s name, over its version, length and playbook.
filestring'Larkspur_MSA_v4.pdf'The file the agent read, in the first step.
playbookstring'SaaS vendor v3'The playbook the clauses were checked against.
draftMonthsnumber3Their draft’s liability cap in months (0 to 25): where the red ring sits.
redlineMonthsnumber12The redline’s cap in months: where the lit dot sits, and where the arc ends.
playbookMonthsnumber12The playbook minimum in months: the dashed line, with grey contracts below it.
defaultPromptstring—What the composer holds at first.
onDecision(decision: 'open' | 'accepted' | 'rejected') => void—Called when the redline is accepted, rejected or opened again with Undo.
onSend(prompt: string, mode: 'redline' | 'compare') => void—Called with the composer’s text and mode when it is sent (the button or Enter).
autoplaybooleantrueWhile on screen and untouched, a demo plays the review from the start, over and over. Any real input hands control to the reader for six seconds.
defaultState{ steps?: 1 | 2 | 3; tab?: 'review' | 'redlines'; decision?: 'open' | 'accepted' | 'rejected'; mode?: 'redline' | 'compare'; drafting?: 'running' | 'stopped' | 'done'; hoverMonths?: number }—Opens in this state, with the demo off: the steps still reading (1) or checking (2), a tab, a settled decision, the composer’s mode, the drafting line stopped or done, or the chart’s tooltip on the column nearest hoverMonths.
colorScheme'light' | 'dark' | 'system'—Light or dark colours, or the reader’s; without it, the page’s color-scheme.
classNamestring—Added to the stage.

The complete source behind this demo. Open source under the MIT license, © 2026 Bruno Delorence: copy it, change it, ship it.

redline-review.tsx
// Copyright (c) 2026 Bruno Delorence. MIT License.'use client';import {  memo,  useEffect,  useEffectEvent,  useId,  useRef,  useState,  type CSSProperties,  type FormEvent,  type KeyboardEvent as ReactKeyboardEvent,  type PointerEvent as ReactPointerEvent,} from 'react';import {  AXIS_Y,  COLUMNS,  DOT_R,  DOTS,  PER_DOT,  PITCH,  PX_PER_MONTH,
market.ts
// Copyright (c) 2026 Bruno Delorence. MIT License./* * The market the liability cap is weighed against: 1,458 invented SaaS master agreements, one dot * for every two, stacked in columns along a 0 to 26 month axis. Each column stands for a sliver of * months (the dots' pitch over the axis' scale) and stacks above and below the axis line. * * Everything here is in the chart's own units: the drawing is VIEW_W × VIEW_H and the SVG scales * it to the card's width. *//** The drawing's size, and where its axis runs. */export const VIEW_W = 527;export const VIEW_H = 118;export const AXIS_Y = 61.75;/** Px per month along the axis (0 months sits at x = 0), and the distance between dots. */export const PX_PER_MONTH = 20.333;export const PITCH = 4.6;export const DOT_R = 1.8;/** Contracts per dot. */export const PER_DOT = 2;/** * The columns, from 0.92 to 25.56 months, each a [dots above, dots below] pair besides the one on
use-autoplay.ts
// Copyright (c) 2026 Bruno Delorence. MIT License.'use client';import { useCallback, useEffect, useRef, useState } from 'react';/* * When a demo may play on its own: while part of it is on screen and the tab is visible, never * under reduced motion, and not while the reader is using it. `take()` hands it to the reader; it * takes back over `idle` ms after their last input. `live` is the same without the reader: what * moves by itself (a progress bar) may move then. */export function useAutoplay<T extends HTMLElement>(idle = 6000) {  const ref = useRef<T>(null);  const [visible, setVisible] = useState(false);  const [reduced, setReduced] = useState(true);  const [taken, setTaken] = useState(false);  const timer = useRef(0);  useEffect(() => {    const element = ref.current;    if (!element) return;    let onScreen = false;    const update = () => setVisible(onScreen && !document.hidden);
redline-review.module.css
/* Redline review (redline-review.tsx): a graphite stage with the card in its middle, 582 px at   most, near-black at night and white by day; inside it a darker well for the chart and a grey   tray for the drafting line and the composer.   Colours are the tokens below, light-dark(light, dark): the component takes the color-scheme it   inherits from the page (light unless the page says otherwise), or the one its colorScheme prop   sets (data-color-scheme).   Motion is transforms and opacity: the dots fall by a translate, the arc draws through a mask's   dash offset, the markers pop by a scale and the progress line stretches by scaleX. */.root {  /* The stage, the card, the chart's well, the drafting tray and the composer. */  --stage: #ececec;  --card: #fff;  --card-ring: rgb(0 0 0 / 0.07);  --well: #fafafa;  --well-line: #ececec;  --tray: #f4f4f4;  --tray-line: #e6e6e6;  --composer-top: #fff;  --composer-bottom: #f7f7f7;  --composer-line: #e4e4e4;  --composer-edge: #d9d9d9;
  • The card is an article named by the contract; the steps are an ordered list of real text, and their icons are hidden from assistive technology.
  • Review and Redlines are tabs: arrow keys, Home and End move between them, and each controls the panel below.
  • The chart is an image with a full description: how many contracts it shows, and where their draft, the playbook minimum and the redline stand. The hover tooltip only repeats a column’s count for a mouse or pen.
  • Accept, Reject, Undo, Cancel and Send are real buttons, and Redline and Compare a radio group. The drafting line is a progressbar, and decisions, sending and drafting announce themselves in a polite live region.
  • Every control shows a focus ring. Under reduced motion nothing animates or moves on its own, and the demo stays off.