Blocks · Marketing
Comparison table
Your product against its alternatives, one column each with ticks; your column drawn out, the head held while the rows scroll, in a scroll box of its own.
5.4% of the 2,073 sites in The UI Index have a table (112 sites).
Compare
Northwind against the alternatives
What you get with each, feature by feature.
| NorthwindFrom $12 a monthStart free | LedgerlyAccounting suiteMoving from Ledgerly | SheetworkSpreadsheetsMoving from Sheetwork | PaperbookBy handMoving from paper | |
|---|---|---|---|---|
| Invoices in your language | ||||
| Bank feeds | ||||
| Tax return export | Add-on | Manual | Manual | |
| Receipts by photo | ||||
| Unlimited clients | ||||
| Works offline | ||||
| Shared with your accountant | Manual | |||
| Recurring invoices | ||||
| Multi-currency | Add-on | Manual | ||
| Audit trail | ||||
| Roles and permissions | ||||
| Export everything, any time |
Based on each product's public plans, October 2026.
Compare
Northwind against the alternatives
What you get with each, feature by feature.
| NorthwindFrom $12 a monthStart free | LedgerlyAccounting suiteMoving from Ledgerly | SheetworkSpreadsheetsMoving from Sheetwork | PaperbookBy handMoving from paper | |
|---|---|---|---|---|
| Invoices in your language | ||||
| Bank feeds | ||||
| Tax return export | Add-on | Manual | Manual | |
| Receipts by photo | ||||
| Unlimited clients | ||||
| Works offline | ||||
| Shared with your accountant | Manual | |||
| Recurring invoices | ||||
| Multi-currency | Add-on | Manual | ||
| Audit trail | ||||
| Roles and permissions | ||||
| Export everything, any time |
Based on each product's public plans, October 2026.
Both previews render the same markup; the HTML tab below is that markup without a framework.
Install
- React
- npx shadcn@
latest add https:// www.themes.dev/ ui/ r/ comparison-table.json - Astro
- npx shadcn@
latest add https:// www.themes.dev/ ui/ r/ astro/ comparison-table.json
Or copy the files below into your project. They need clsx, tailwind-merge and the components they import, and the themes.dev UI theme. The HTML needs only the compiled CSS.
/** A comparison table: the product against its alternatives, one column each, with ticks. The product's column is
* drawn out on an inverted ground. It is a real <table> in a scroll box of its own (focusable, named by its caption):
* the head stays in view while the rows scroll, the feature names stay at the left edge on a phone, and the last
* row has room below it so it is never cut off. */
export type ComparisonColumn = {
/** The product or alternative: at most 32 characters. */
name: string;
/** A line under the name (a price or a kind of tool), at most 40 characters. */
note?: string;
/** The first column is drawn out. At most one. */
featured?: boolean;
/** A button under the featured column's name. */
action?: { label: string; href: string };
/** A text link under an alternative's name. */
link?: { label: string; href: string };
};
export type ComparisonRow = {
/** The feature: at most 60 characters. */
label: string;
/** One per column, in column order: yes, no, or a short text (Manual). */
values: string[];
};
export type ComparisonTableContent = {
kicker?: string;
title: string;
description?: string;
columns: ComparisonColumn[];
rows: ComparisonRow[];
/** Names the table and the scroll box. Default: the title. */
caption?: string;
/** Small print under the table. */
note?: string;
/** Unique on the page; ids the heading and the caption. */
id: string;
};
/** A value: yes and no draw a mark with a text alternative, anything else is text. */
export const comparisonKind = (value: string) => {
const v = value.trim().toLowerCase();
return v === 'yes' ? 'yes' : v === 'no' ? 'no' : 'text';
};
export const COMPARISON_COPY = { yes: 'Included', no: 'Not included' };
const featured = 'inverted bg-background text-foreground';
export const comparisonTable = {
root: '@container bg-background px-6 py-14 text-foreground',
inner: 'mx-auto flex max-w-6xl flex-col gap-10',
body: 'flex min-w-0 flex-col gap-4',
region: 'relative overflow-x-auto rounded-lg border [background:linear-gradient(to_right,var(--background)_50%,transparent)_left/2rem_100%_no-repeat_local,linear-gradient(to_left,var(--background)_50%,transparent)_right/2rem_100%_no-repeat_local,radial-gradient(farthest-side_at_0_50%,color-mix(in_oklab,var(--foreground)_18%,transparent),transparent)_left/0.75rem_100%_no-repeat_scroll,radial-gradient(farthest-side_at_100%_50%,color-mix(in_oklab,var(--foreground)_18%,transparent),transparent)_right/0.75rem_100%_no-repeat_scroll,var(--background)]',
table: 'w-full table-fixed border-separate border-spacing-0 text-start text-sm max-md:w-max max-md:table-auto md:min-w-[40rem] [&>tbody>tr:last-child>*]:pb-6 [&>tbody>tr:last-child>.inverted]:rounded-b-lg',
caption: 'sr-only',
// The corner is sticky both ways; the feature names are sticky at the start edge (a rule beside them on a phone).
corner: 'sticky start-0 top-0 z-30 bg-background shadow-[inset_-1px_-1px_0_var(--border)] max-md:w-28 max-md:min-w-28 md:w-1/4 md:shadow-[inset_0_-1px_0_var(--border)]',
colHead: (isFeatured?: boolean) =>
`sticky top-0 z-20 px-4 pt-5 pb-4 text-start align-top font-normal shadow-[inset_0_-1px_0_var(--border)] max-md:w-24 max-md:min-w-24 max-sm:px-2 lg:px-6 ${isFeatured ? `${featured} rounded-t-lg` : 'bg-background'}`,
colStack: 'flex flex-col items-start gap-2',
colName: 'text-base font-semibold text-foreground',
colNote: 'text-sm font-normal text-muted-foreground',
colAction: 'mt-1 w-full max-sm:px-1.5 max-sm:text-[13px]',
colLink: 'mt-1',
rowHead: 'sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6',
cell: (isFeatured?: boolean) => `border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ${isFeatured ? featured : ''}`,
yes: 'size-5 text-foreground',
no: 'size-5 text-muted-foreground',
note: 'text-sm text-pretty text-muted-foreground',
};import { Button } from '@/components/ui/button';
import { Icon } from '@/components/ui/icon';
import { Link } from '@/components/ui/link';
import { SectionHeader } from '@/components/ui/section-header';
import { cn } from '@/lib/utils';
import { COMPARISON_COPY, comparisonKind, comparisonTable as c, type ComparisonTableContent } from './comparison-table.classes';
export type ComparisonTableProps = ComparisonTableContent & { className?: string };
/** The product against its alternatives in a table with ticks: its column drawn out, the head held while the rows scroll. */
export function ComparisonTable({ kicker, title, description, columns, rows, caption, note, id, className }: ComparisonTableProps) {
return (
<section aria-labelledby={`${id}-title`} className={cn(c.root, className)}>
<div className={c.inner}>
<SectionHeader id={`${id}-title`} kicker={kicker} title={title} description={description} />
<div className={c.body}>
<div className={c.region} role="region" aria-labelledby={`${id}-caption`} tabIndex={0}>
<table className={c.table}>
<caption id={`${id}-caption`} className={c.caption}>{caption ?? title}</caption>
<thead>
<tr>
<td className={c.corner} />
{columns.map((col) => (
<th key={col.name} scope="col" className={c.colHead(col.featured)}>
<span className={c.colStack}>
<span className={c.colName}>{col.name}</span>
{col.note && <span className={c.colNote}>{col.note}</span>}
{col.action && <Button href={col.action.href} className={c.colAction}>{col.action.label}</Button>}
{col.link && <Link href={col.link.href} variant="standalone" className={c.colLink}>{col.link.label}</Link>}
</span>
</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row) => (
<tr key={row.label}>
<th scope="row" className={c.rowHead}>{row.label}</th>
{columns.map((col, i) => {
const value = row.values[i] ?? 'no';
const kind = comparisonKind(value);
return (
<td key={col.name} className={c.cell(col.featured)}>
{kind === 'yes' ? <Icon name="check" label={COMPARISON_COPY.yes} className={c.yes} /> : kind === 'no' ? <Icon name="minus" label={COMPARISON_COPY.no} className={c.no} /> : value}
</td>
);
})}
</tr>
))}
</tbody>
</table>
</div>
{note && <p className={c.note}>{note}</p>}
</div>
</div>
</section>
);
}---
import Button from '@/components/ui/button.astro';
import Icon from '@/components/ui/icon.astro';
import Link from '@/components/ui/link.astro';
import SectionHeader from '@/components/ui/section-header.astro';
import { cn } from '@/lib/utils';
import { COMPARISON_COPY, comparisonKind, comparisonTable as c, type ComparisonTableContent } from './comparison-table.classes';
type Props = ComparisonTableContent & { class?: string };
const { kicker, title, description, columns, rows, caption, note, id, class: className } = Astro.props;
---
<section aria-labelledby={`${id}-title`} class={cn(c.root, className)}>
<div class={c.inner}>
<SectionHeader id={`${id}-title`} kicker={kicker} title={title} description={description} />
<div class={c.body}>
<div class={c.region} role="region" aria-labelledby={`${id}-caption`} tabindex="0">
<table class={c.table}>
<caption id={`${id}-caption`} class={c.caption}>{caption ?? title}</caption>
<thead>
<tr>
<td class={c.corner}></td>
{
columns.map((col) => (
<th scope="col" class={c.colHead(col.featured)}>
<span class={c.colStack}>
<span class={c.colName}>{col.name}</span>
{col.note && <span class={c.colNote}>{col.note}</span>}
{col.action && <Button href={col.action.href} class={c.colAction}>{col.action.label}</Button>}
{col.link && <Link href={col.link.href} variant="standalone" class={c.colLink}>{col.link.label}</Link>}
</span>
</th>
))
}
</tr>
</thead>
<tbody>
{
rows.map((row) => (
<tr>
<th scope="row" class={c.rowHead}>{row.label}</th>
{columns.map((col, i) => {
const value = row.values[i] ?? 'no';
const kind = comparisonKind(value);
return (
<td class={c.cell(col.featured)}>
{kind === 'yes' ? <Icon name="check" label={COMPARISON_COPY.yes} class={c.yes} /> : kind === 'no' ? <Icon name="minus" label={COMPARISON_COPY.no} class={c.no} /> : value}
</td>
);
})}
</tr>
))
}
</tbody>
</table>
</div>
{note && <p class={c.note}>{note}</p>}
</div>
</div>
</section>/** A comparison table: the product against its alternatives, one column each, with ticks. The product's column is
* drawn out on an inverted ground. It is a real <table> in a scroll box of its own (focusable, named by its caption):
* the head stays in view while the rows scroll, the feature names stay at the left edge on a phone, and the last
* row has room below it so it is never cut off. */
export type ComparisonColumn = {
/** The product or alternative: at most 32 characters. */
name: string;
/** A line under the name (a price or a kind of tool), at most 40 characters. */
note?: string;
/** The first column is drawn out. At most one. */
featured?: boolean;
/** A button under the featured column's name. */
action?: { label: string; href: string };
/** A text link under an alternative's name. */
link?: { label: string; href: string };
};
export type ComparisonRow = {
/** The feature: at most 60 characters. */
label: string;
/** One per column, in column order: yes, no, or a short text (Manual). */
values: string[];
};
export type ComparisonTableContent = {
kicker?: string;
title: string;
description?: string;
columns: ComparisonColumn[];
rows: ComparisonRow[];
/** Names the table and the scroll box. Default: the title. */
caption?: string;
/** Small print under the table. */
note?: string;
/** Unique on the page; ids the heading and the caption. */
id: string;
};
/** A value: yes and no draw a mark with a text alternative, anything else is text. */
export const comparisonKind = (value: string) => {
const v = value.trim().toLowerCase();
return v === 'yes' ? 'yes' : v === 'no' ? 'no' : 'text';
};
export const COMPARISON_COPY = { yes: 'Included', no: 'Not included' };
const featured = 'inverted bg-background text-foreground';
export const comparisonTable = {
root: '@container bg-background px-6 py-14 text-foreground',
inner: 'mx-auto flex max-w-6xl flex-col gap-10',
body: 'flex min-w-0 flex-col gap-4',
region: 'relative overflow-x-auto rounded-lg border [background:linear-gradient(to_right,var(--background)_50%,transparent)_left/2rem_100%_no-repeat_local,linear-gradient(to_left,var(--background)_50%,transparent)_right/2rem_100%_no-repeat_local,radial-gradient(farthest-side_at_0_50%,color-mix(in_oklab,var(--foreground)_18%,transparent),transparent)_left/0.75rem_100%_no-repeat_scroll,radial-gradient(farthest-side_at_100%_50%,color-mix(in_oklab,var(--foreground)_18%,transparent),transparent)_right/0.75rem_100%_no-repeat_scroll,var(--background)]',
table: 'w-full table-fixed border-separate border-spacing-0 text-start text-sm max-md:w-max max-md:table-auto md:min-w-[40rem] [&>tbody>tr:last-child>*]:pb-6 [&>tbody>tr:last-child>.inverted]:rounded-b-lg',
caption: 'sr-only',
// The corner is sticky both ways; the feature names are sticky at the start edge (a rule beside them on a phone).
corner: 'sticky start-0 top-0 z-30 bg-background shadow-[inset_-1px_-1px_0_var(--border)] max-md:w-28 max-md:min-w-28 md:w-1/4 md:shadow-[inset_0_-1px_0_var(--border)]',
colHead: (isFeatured?: boolean) =>
`sticky top-0 z-20 px-4 pt-5 pb-4 text-start align-top font-normal shadow-[inset_0_-1px_0_var(--border)] max-md:w-24 max-md:min-w-24 max-sm:px-2 lg:px-6 ${isFeatured ? `${featured} rounded-t-lg` : 'bg-background'}`,
colStack: 'flex flex-col items-start gap-2',
colName: 'text-base font-semibold text-foreground',
colNote: 'text-sm font-normal text-muted-foreground',
colAction: 'mt-1 w-full max-sm:px-1.5 max-sm:text-[13px]',
colLink: 'mt-1',
rowHead: 'sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6',
cell: (isFeatured?: boolean) => `border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ${isFeatured ? featured : ''}`,
yes: 'size-5 text-foreground',
no: 'size-5 text-muted-foreground',
note: 'text-sm text-pretty text-muted-foreground',
};<section aria-labelledby="ex-comparison-title" class="@container bg-background px-6 py-14 text-foreground">
<div class="mx-auto flex max-w-6xl flex-col gap-10">
<header class="@container w-full">
<div class="flex flex-col gap-4 @2xl:flex-row @2xl:items-end @2xl:justify-between">
<div>
<p class="text-sm font-medium text-muted-foreground">Compare</p>
<h2 id="ex-comparison-title" class="mt-2 text-3xl font-semibold tracking-[-0.03em] text-balance @2xl:text-4xl">Northwind against the alternatives</h2>
<p class="mt-3 max-w-xl text-base text-pretty text-muted-foreground">What you get with each, feature by feature.</p>
</div>
</div>
</header>
<div class="flex min-w-0 flex-col gap-4">
<div class="relative overflow-x-auto rounded-lg border [background:linear-gradient(to_right,var(--background)_50%,transparent)_left/2rem_100%_no-repeat_local,linear-gradient(to_left,var(--background)_50%,transparent)_right/2rem_100%_no-repeat_local,radial-gradient(farthest-side_at_0_50%,color-mix(in_oklab,var(--foreground)_18%,transparent),transparent)_left/0.75rem_100%_no-repeat_scroll,radial-gradient(farthest-side_at_100%_50%,color-mix(in_oklab,var(--foreground)_18%,transparent),transparent)_right/0.75rem_100%_no-repeat_scroll,var(--background)]" role="region" aria-labelledby="ex-comparison-caption" tabindex="0">
<table class="w-full table-fixed border-separate border-spacing-0 text-start text-sm max-md:w-max max-md:table-auto md:min-w-[40rem] [&>tbody>tr:last-child>*]:pb-6 [&>tbody>tr:last-child>.inverted]:rounded-b-lg">
<caption id="ex-comparison-caption" class="sr-only">Northwind compared with Ledgerly, Sheetwork and Paperbook, feature by feature</caption>
<thead>
<tr>
<td class="sticky start-0 top-0 z-30 bg-background shadow-[inset_-1px_-1px_0_var(--border)] max-md:w-28 max-md:min-w-28 md:w-1/4 md:shadow-[inset_0_-1px_0_var(--border)]">
</td>
<th scope="col" class="sticky top-0 z-20 px-4 pt-5 pb-4 text-start align-top font-normal shadow-[inset_0_-1px_0_var(--border)] max-md:w-24 max-md:min-w-24 max-sm:px-2 lg:px-6 inverted bg-background text-foreground rounded-t-lg">
<span class="flex flex-col items-start gap-2">
<span class="text-base font-semibold text-foreground">Northwind</span>
<span class="text-sm font-normal text-muted-foreground">From $12 a month</span>
<a href="#" class="inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,background-color,border-color,transform] select-none active:scale-[0.97] aria-busy:cursor-progress disabled:pointer-events-none disabled:opacity-50 aria-busy:disabled:opacity-80 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-[color-mix(in_oklab,var(--primary)_var(--hover-amount),var(--background))] active:bg-[color-mix(in_oklab,var(--primary)_var(--press-amount),var(--press-mix))] h-9 px-4 mt-1 w-full max-sm:px-1.5 max-sm:text-[13px]">
Start free
</a>
</span>
</th>
<th scope="col" class="sticky top-0 z-20 px-4 pt-5 pb-4 text-start align-top font-normal shadow-[inset_0_-1px_0_var(--border)] max-md:w-24 max-md:min-w-24 max-sm:px-2 lg:px-6 bg-background">
<span class="flex flex-col items-start gap-2">
<span class="text-base font-semibold text-foreground">Ledgerly</span>
<span class="text-sm font-normal text-muted-foreground">Accounting suite</span>
<a href="#" class="rounded-sm font-medium text-foreground underline-offset-4 transition-[color,text-decoration-color,background-color] active:bg-accent [&_svg]:size-3.5 [&_svg]:shrink-0 [&_svg]:transition-transform group inline-flex min-h-6 items-center gap-1.5 hover:underline active:decoration-2 hover:[&_svg]:translate-x-0.5 mt-1">
Moving from Ledgerly
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"></path></svg>
</a>
</span>
</th>
<th scope="col" class="sticky top-0 z-20 px-4 pt-5 pb-4 text-start align-top font-normal shadow-[inset_0_-1px_0_var(--border)] max-md:w-24 max-md:min-w-24 max-sm:px-2 lg:px-6 bg-background">
<span class="flex flex-col items-start gap-2">
<span class="text-base font-semibold text-foreground">Sheetwork</span>
<span class="text-sm font-normal text-muted-foreground">Spreadsheets</span>
<a href="#" class="rounded-sm font-medium text-foreground underline-offset-4 transition-[color,text-decoration-color,background-color] active:bg-accent [&_svg]:size-3.5 [&_svg]:shrink-0 [&_svg]:transition-transform group inline-flex min-h-6 items-center gap-1.5 hover:underline active:decoration-2 hover:[&_svg]:translate-x-0.5 mt-1">
Moving from Sheetwork
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"></path></svg>
</a>
</span>
</th>
<th scope="col" class="sticky top-0 z-20 px-4 pt-5 pb-4 text-start align-top font-normal shadow-[inset_0_-1px_0_var(--border)] max-md:w-24 max-md:min-w-24 max-sm:px-2 lg:px-6 bg-background">
<span class="flex flex-col items-start gap-2">
<span class="text-base font-semibold text-foreground">Paperbook</span>
<span class="text-sm font-normal text-muted-foreground">By hand</span>
<a href="#" class="rounded-sm font-medium text-foreground underline-offset-4 transition-[color,text-decoration-color,background-color] active:bg-accent [&_svg]:size-3.5 [&_svg]:shrink-0 [&_svg]:transition-transform group inline-flex min-h-6 items-center gap-1.5 hover:underline active:decoration-2 hover:[&_svg]:translate-x-0.5 mt-1">
Moving from paper
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"></path></svg>
</a>
</span>
</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Invoices in your language
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Bank feeds
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Tax return export
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">Add-on</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">Manual</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">Manual</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Receipts by photo
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Unlimited clients
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Works offline
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Shared with your accountant
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">Manual</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Recurring invoices
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Multi-currency
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">Add-on</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">Manual</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Audit trail
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Roles and permissions
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
</tr>
<tr>
<th scope="row" class="sticky start-0 z-10 border-t bg-background px-4 py-3 text-start align-top font-normal text-foreground shadow-[inset_-1px_0_0_var(--border)] max-sm:px-2 md:shadow-none lg:px-6">
Export everything, any time
</th>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 inverted bg-background text-foreground">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Not included" class="inline-block shrink-0 size-5 text-muted-foreground"><path d="M3.5 8h9"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
<td class="border-t px-4 py-3 align-top text-muted-foreground max-sm:px-2 lg:px-6 ">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Included" class="inline-block shrink-0 size-5 text-foreground"><path d="M3.5 8.5l3 3 6-7"></path></svg>
</td>
</tr>
</tbody>
</table>
</div>
<p class="text-sm text-pretty text-muted-foreground">Based on each product's public plans, October 2026.</p>
</div>
</div>
</section>