Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 20ace674a35b849ffda63b30
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
src/styles.css
/* Split. Shared look comes from /kit.css; this is the app's own layout. */
.app-mode .landing-only { display: none !important; }
:root:not(.app-mode) #app:empty { display: none; }
/* The group fills the first screen, so the footer never jumps while it loads. */
.app-mode #app { min-height: calc(100vh - 64px - var(--safe-top)); min-height: calc(100dvh - 64px - var(--safe-top)); }
.field > label { font: var(--type-label); color: #33363c; }
.page-pad { padding-block: 56px 80px; }
.narrow { max-width: 640px; }
.small { font-size: 14px; }
.num { font-variant-numeric: tabular-nums; }
.pos { color: #2f6b45; }
.neg { color: var(--brick); }
.label-caps { font: var(--type-caps); letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.state-title { font: var(--type-title); letter-spacing: -.02em; color: var(--ink-solid); text-wrap: balance; }
.link-btn { position: relative; padding: 0; border: 0; background: none; color: var(--cobalt); font: 600 15px/1.3 var(--sans); cursor: pointer; }
@media (hover: hover) { .link-btn:hover { color: var(--cobalt-ink); } }
@media (pointer: coarse) { .link-btn::before { content: ""; position: absolute; inset: -12px -8px; } }
/* Landing hero with the start column */
.app-hero { padding: 48px 0 72px; border-bottom: 1px solid var(--line); }
.app-hero-grid { display: grid; grid-template-columns: minmax(0, 1fr) 440px; gap: 56px; align-items: center; }
.app-hero h1 { margin-top: 14px; }
.app-hero .lede { margin-top: 18px; }
.hero-points { list-style: none; padding: 0; margin-top: 26px; display: grid; gap: 10px; font-size: 15.5px; color: #3c4048; }
.hero-points li { display: flex; gap: 10px; align-items: baseline; }
.hero-points li::before { content: ""; flex: 0 0 8px; height: 8px; border-radius: 50%; background: var(--cobalt); transform: translateY(-2px); }
.hero-art { width: 200px; height: auto; margin-bottom: -8px; }
@media (max-width: 900px) {
.app-hero { padding: 28px 0 48px; }
.app-hero-grid { grid-template-columns: minmax(0, 1fr); gap: 28px; }
.hero-art { width: 132px; }
/* A returning visitor sees their groups first. */
.app-hero-grid:has(.your-groups) > #create { order: -1; }
}
.start { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
.your-groups { padding: 18px 12px 8px 20px; }
.your-groups ul { list-style: none; padding: 0; margin-top: 6px; max-height: 296px; overflow-y: auto; }
.your-groups li { display: flex; align-items: center; gap: 4px; border-top: 1px solid var(--line); }
.your-groups li:first-child { border-top: 0; }
.your-group { flex: 1; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 48px; color: var(--ink); font: 600 15.5px/1.3 var(--sans); }
.your-group span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.your-group svg { flex: none; color: var(--faint); transition: transform var(--motion-fast) var(--ease-out); }
@media (hover: hover) { .your-group:hover { color: var(--ink-solid); } .your-group:hover svg { transform: translateX(2px); color: var(--ink); } }
.your-groups .btn-icon { color: var(--faint); }
.create { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; padding: 24px; }
.create-title { font: var(--type-title); letter-spacing: -.02em; color: var(--ink-solid); }
.chips-input { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; min-height: 46px; padding: 6px 8px; border: 1px solid var(--line-strong); border-radius: var(--radius); background: #fff; cursor: text; transition: border-color var(--motion-fast) var(--ease-out), box-shadow var(--motion-fast) var(--ease-out); }
.chips-input:focus-within { border-color: var(--cobalt); box-shadow: 0 0 0 3px #2e5ec41f; }
.chips-input.invalid { border-color: var(--brick); }
.chips-input input { flex: 1; min-width: 120px; height: 32px; border: 0; outline: 0; background: transparent; font: var(--type-input); padding: 0 4px; }
.chips-input input::placeholder { color: #9a9ea6; }
.person-chip { display: inline-flex; align-items: center; gap: 2px; height: 32px; padding: 0 4px 0 12px; border-radius: 999px; background: var(--cobalt-wash); color: var(--cobalt-ink); font: 600 14px var(--sans); animation: oa-enter var(--motion-fast) var(--ease-out); }
.person-chip button { position: relative; display: grid; place-items: center; width: 26px; height: 26px; border: 0; border-radius: 50%; background: transparent; color: inherit; cursor: pointer; }
@media (hover: hover) { .person-chip button:hover { background: #d4e0f6; } }
@media (pointer: coarse) { .person-chip button::before { content: ""; position: absolute; inset: -9px; } }
/* Landing sections */
.steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 28px; margin-top: 28px; padding: 0; counter-reset: step; }
.steps li { list-style: none; counter-increment: step; }
.steps li::before { content: counter(step); display: grid; place-items: center; width: 34px; height: 34px; margin-bottom: 14px; border-radius: 50%; background: var(--ink-solid); color: #fff; font: 650 15px var(--sans); }
.steps h3 { font: 600 18px/1.3 var(--sans); }
.steps p { margin-top: 8px; color: #4a4e56; }
@media (max-width: 760px) { .steps { grid-template-columns: minmax(0, 1fr); gap: 22px; } }
.features { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: 12px; margin-top: 26px; padding: 0; list-style: none; }
.features li { padding: 18px 20px; border: 1px solid var(--line); border-radius: 14px; background: #fff; }
.features strong { display: block; font: 600 16px/1.35 var(--sans); color: var(--ink-solid); }
.features span { display: block; margin-top: 6px; color: #4a4e56; font-size: 15px; line-height: 1.55; }
.not-list { margin-top: 18px; padding-left: 1.2em; display: grid; gap: 8px; color: #3d4148; max-width: 70ch; }
.api-grid { display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: 40px; align-items: start; }
.api-grid .code { font-size: 12.5px; }
.deep-links { list-style: none; padding: 0; margin-top: 10px; display: grid; gap: 12px; }
.deep-links code { display: block; width: fit-content; max-width: 100%; padding: 6px 10px; border-radius: 8px; background: var(--panel); border: 1px solid var(--panel-line); font: 13px/1.5 var(--mono); overflow-wrap: anywhere; }
.deep-links span { display: block; margin-top: 4px; color: #4a4e56; font-size: 15px; }
@media (max-width: 900px) { .api-grid { grid-template-columns: minmax(0, 1fr); } }
.more-apps { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; list-style: none; padding: 0; margin-top: 22px; }
.more-apps a { display: grid; gap: 4px; padding: 16px 18px; border: 1px solid var(--line); border-radius: 14px; background: #fff; color: var(--ink); transition: border-color var(--motion-fast) var(--ease-out), box-shadow var(--motion-fast) var(--ease-out); }
@media (hover: hover) { .more-apps a:hover { border-color: var(--line-strong); box-shadow: var(--shadow); color: var(--ink); } }
.more-apps strong { font: 400 22px/1.1 var(--serif); }
.more-apps span { color: var(--muted); font-size: 14px; }
.compare td:first-child { font-weight: 600; }
/* Group page */
.group-head { border-bottom: 1px solid var(--line); }
.group-head-inner { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px 24px; flex-wrap: wrap; padding: 30px 0 22px; }
.group-title-block { min-width: 0; }
.group-title { font: 350 clamp(30px, 4vw, 44px)/1.08 var(--serif); letter-spacing: -.03em; color: var(--ink-solid); text-wrap: balance; overflow-wrap: anywhere; }
.group-sub { margin-top: 6px; color: var(--muted); font-variant-numeric: tabular-nums; }
.group-actions { display: flex; align-items: center; gap: 8px; }
.group-body { display: flex; flex-direction: column; gap: 20px; padding-block: 22px 80px; }
.invite { display: flex; align-items: center; justify-content: space-between; gap: 10px 14px; flex-wrap: wrap; }
.you { display: flex; align-items: center; justify-content: space-between; gap: 18px 24px; flex-wrap: wrap; padding: 20px 22px; }
.you-main, .who { display: grid; gap: 4px; min-width: 0; }
.you-who { color: var(--muted); font-size: 15px; }
.you-who strong { color: var(--ink); font-weight: 600; }
.you-who .link-btn { margin-left: 6px; }
.you-balance { font: 380 30px/1.15 var(--serif); letter-spacing: -.02em; font-variant-numeric: tabular-nums; color: var(--ink-solid); }
.you-balance.pos { color: #2f6b45; }
.you-balance.neg { color: var(--brick); }
.you-next { display: flex; flex-wrap: wrap; gap: 2px 16px; margin-top: 2px; color: #4a4e56; font-size: 15px; font-variant-numeric: tabular-nums; }
.who-title { font: var(--type-title); letter-spacing: -.02em; color: var(--ink-solid); }
.who-names { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.you-actions { display: flex; gap: 10px; }
.you-actions .btn, .group-bar .btn { min-width: 150px; }
.group-bar { display: none; }
@media (max-width: 720px) {
.group-head-inner { padding: 22px 0 18px; }
.you { padding: 18px; }
.you-actions { display: none; }
.group-bar { display: flex; }
.group-bar .btn { min-width: 0; }
/* The page fills the screen, so the action bar rests at the bottom even when the list is short. */
.group { display: flex; flex-direction: column; min-height: calc(100vh - 64px - var(--safe-top)); min-height: calc(100dvh - 64px - var(--safe-top)); }
.group-body { flex: 1; padding-bottom: 0; }
.group-bar { margin-top: auto; }
}
.tabs { display: none; gap: 4px; border-bottom: 1px solid var(--line); }
.tabs button { display: inline-flex; align-items: center; gap: 8px; min-height: 46px; padding: 0 12px; margin-bottom: -1px; border: 0; border-bottom: 2px solid transparent; background: none; color: var(--muted); font: var(--type-control); cursor: pointer; transition: color var(--motion-fast) var(--ease-out), border-color var(--motion-fast) var(--ease-out); }
.tabs button[aria-selected="true"] { color: var(--ink-solid); border-bottom-color: var(--ink-solid); }
.tab-count { display: inline-flex; align-items: center; min-height: 22px; padding: 0 8px; border-radius: 999px; background: var(--brick-wash); color: #7c2f1d; font: 600 12.5px/1 var(--sans); }
.group-grid { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 28px; align-items: start; }
.balances { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; position: sticky; top: 84px; }
@media (max-width: 900px) {
.tabs { display: flex; }
.group-grid { grid-template-columns: minmax(0, 1fr); }
.group-grid[data-tab="expenses"] .balances, .group-grid[data-tab="balances"] .expenses { display: none; }
.group-grid > * { animation: oa-fade-in var(--motion) var(--ease-out); }
.balances { position: static; }
.skeleton-aside { display: none; }
}
.search { position: relative; display: block; margin-bottom: 16px; }
.search svg { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--faint); pointer-events: none; }
.search .input { padding-left: 42px; }
.no-match { padding: 20px 4px; }
@media (max-width: 720px) { .expenses .empty-state > img { width: 120px; } }
.day + .day { margin-top: 20px; }
.day-label { margin-bottom: 8px; font: var(--type-caps); letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.expense-list { list-style: none; padding: 0; border: 1px solid var(--line); border-radius: 14px; background: #fff; overflow: hidden; }
.expense-list > * + * { border-top: 1px solid var(--line); }
.expense { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; gap: 14px; align-items: center; width: 100%; min-height: 68px; padding: 12px 16px; border: 0; background: none; color: inherit; text-align: left; cursor: pointer; transition: background-color var(--motion-fast) var(--ease-out); }
@media (hover: hover) { .expense:hover { background: #fafaf7; } }
.expense:active { background: #f4f3ee; }
.expense:focus-visible { outline-offset: -3px; border-radius: 12px; }
.expense-icon { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px; background: var(--paper-warm); border: 1px solid #ece2cf; color: #6b5a3c; font: 450 19px/1 var(--serif); }
.expense-icon.payment { background: var(--sage-wash); border-color: #cfe2d4; color: #2f5a3c; }
.expense-main { display: grid; gap: 3px; min-width: 0; }
.expense-title { font: 600 15.5px/1.3 var(--sans); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.expense-meta { font-size: 13.5px; line-height: 1.3; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.expense-side { display: grid; justify-items: end; gap: 2px; text-align: right; white-space: nowrap; font: 600 15px/1.2 var(--sans); }
.side-label { font: 500 12.5px/1.2 var(--sans); color: var(--muted); }
.side-label.pos, .side-label.neg { opacity: .9; }
.balance-card { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 18px 20px; }
.card-title { font: 600 16.5px/1.3 var(--sans); color: var(--ink-solid); }
.balance-list, .settle-list, .people-list { list-style: none; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); }
.balance-list li, .settle-list li { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 0; border-top: 1px solid var(--line); font-size: 15px; }
.balance-list li:first-child, .settle-list li:first-child, .people-list li:first-child { border-top: 0; }
.balance-who { display: grid; gap: 2px; min-width: 0; }
.balance-meta { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.balance-list li > .num { font-weight: 600; font-size: 14.5px; text-align: right; }
.settle-list li > span { min-width: 0; }
.settle-list .num { margin-left: 4px; font-weight: 600; white-space: nowrap; }
.share-summary { justify-self: start; margin-left: -14px; }
/* Sheets */
.two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
.money-input { display: flex; align-items: center; border: 1px solid var(--line-strong); border-radius: var(--radius); background: #fff; transition: border-color var(--motion-fast) var(--ease-out), box-shadow var(--motion-fast) var(--ease-out); }
.money-input:focus-within { border-color: var(--cobalt); box-shadow: 0 0 0 3px #2e5ec41f; }
.money-input.invalid { border-color: var(--brick); }
.money-input span { padding-left: 13px; color: var(--muted); font: 600 13px var(--sans); }
.money-input .input { min-width: 0; border: 0; box-shadow: none !important; font-variant-numeric: tabular-nums; }
.split-field { min-width: 0; margin: 0; padding: 0; border: 0; }
.split-field legend { padding: 0; margin-bottom: 7px; }
.segmented { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px; padding: 4px; border-radius: 12px; background: var(--panel); border: 1px solid var(--panel-line); }
.segmented button { min-height: 40px; padding: 0 6px; border: 0; border-radius: 9px; background: transparent; color: var(--muted); font: var(--type-control); cursor: pointer; transition: background-color var(--motion-fast) var(--ease-out), color var(--motion-fast) var(--ease-out), box-shadow var(--motion-fast) var(--ease-out); }
.segmented button[aria-checked="true"] { background: #fff; color: var(--ink-solid); box-shadow: 0 1px 3px #0000001a; }
.split-list { list-style: none; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); }
.split-list li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; align-items: center; min-height: 52px; border-top: 1px solid var(--line); }
.split-list li:first-child { border-top: 0; }
.split-list[data-mode="shares"] li { grid-template-columns: minmax(0, 1fr) 72px 92px; }
.split-list[data-mode="exact"] li { grid-template-columns: minmax(0, 1fr) 124px; }
.split-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; }
.split-list .check { min-height: 44px; min-width: 0; }
.input-sm { min-height: 40px; padding: 8px 10px; text-align: right; font-variant-numeric: tabular-nums; }
.split-amount { display: grid; justify-items: end; gap: 1px; font: 600 14.5px/1.2 var(--sans); color: #4a4e56; }
.split-amount small { font: 500 12px/1.2 var(--sans); color: var(--muted); }
.field small.pos { color: #2f6b45; }
.field small.neg { color: var(--brick); }
.split-all, .delete-btn { justify-self: start; margin-left: -14px; }
.add-person { display: flex; gap: 8px; }
.add-person .input { flex: 1; min-width: 0; }
.people-list li { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 52px; border-top: 1px solid var(--line); }
.person-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15.5px; }
.person-actions { --gap: 2px; flex-wrap: nowrap; }
.rename { display: flex; align-items: center; gap: 6px; width: 100%; padding: 6px 0; }
.rename .input { flex: 1; min-width: 0; min-height: 40px; padding-block: 8px; }
.link-row { display: flex; align-items: center; gap: 8px; }
.link-code { flex: 1; min-width: 0; padding: 9px 11px; border-radius: 8px; background: var(--panel); border: 1px solid var(--panel-line); font: 13px var(--mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Toasts sit above open sheets, so Undo works from inside one. */
.toast { display: flex; align-items: center; gap: 12px; z-index: 70; }
.toast-action { min-height: 32px; margin: -6px -8px -6px 0; padding: 0 10px; border: 0; border-radius: 8px; background: #ffffff1f; color: #fff; font: var(--type-control); cursor: pointer; }
@media (hover: hover) { .toast-action:hover { background: #ffffff33; } }
/* Loading shapes */
.skeleton-title { height: 40px; width: min(420px, 80%); margin-bottom: 12px; }
.group[aria-busy="true"] .expense { cursor: default; }
Change history
Publish quality pass from owner chat
Bind agent address in policy constants
Quality pass: cleaner group page, phone polish, fuller API, sourced comparison
Bounded source sync
Bounded source sync