/* The Overview flattens while scrolling; its layout stays in normal flow. */

/* Style 5 runs wider than the shared pages: the owner found it narrower
   than style 1, and the card shows the app at about its real size here. */
:root{--measure:1512px}

/* clip, not hidden: .hero's overflow:hidden makes the scene a scroll
   container of its own, which breaks anything measured against the window. */
.scene{padding-bottom:0;overflow:clip}
/* The dots run the scene's whole length and fade out into the paper
   (the owner, 2026-09-30, and 2026-10-23: "fade away ... as it moves into
   the solid part"). They were a 1300px live canvas and a CSS grid below it;
   since the card was cropped the scene is about 1,500px, so one canvas
   covers it, and it fades over its last 420px rather than stopping where
   the canvas ended. */
.scene .hero-dots{bottom:0;height:auto;
  -webkit-mask-image:linear-gradient(#000 calc(100% - 420px),transparent);mask-image:linear-gradient(#000 calc(100% - 420px),transparent)}
.scene .intro{padding-bottom:0}

.card-box{margin-top:44px;perspective:1000px;scroll-margin-top:24px}
/* The card is ~1600px tall; the tilt should look as it did on a card one
   screen tall, so the vanishing point sits where that card's centre was. */
.card-box{perspective-origin:50% 440px}
.card-box .window{max-width:none;transform-origin:50% 0}
/* The opening tilt is here, not only in scene.js: the script runs at the
   end of the page, so the card painted flat first and then tipped back on
   every load (the owner, 2026-10-24: "a quick stutter"). Same conditions
   as scene.js's live check; scene.js sets "none" once the card is flat.
   Only with scripting on, or a page without script would keep it tipped. */
@media (scripting:enabled) and (min-width:1100px) and (min-height:620px) and (prefers-reduced-motion:no-preference){
  .scene .card-box .window{transform:rotateX(14deg) scale(.95)}
}
/* Permanent hints can keep the scroll's old raster scale after a pause.
   Override site.css's window hint here too: both ancestors must release it.
   During a gesture reuse textures; at rest let the browser choose fresh
   raster scales without changing the angle or the fractional position. */
.card-box,.card-box .window{will-change:auto}
.scene.moving .card-box,.scene.moving .window{will-change:transform}
/* A picture of the app, not text: a drag across the hero selected the whole
   card and painted it blue. */
.card-box{-webkit-user-select:none;user-select:none}
.card-box img{-webkit-user-drag:none}
.app-shot{position:relative;overflow:hidden;background:var(--surface)}
/* aspect-ratio:auto: site.css gives .window img a 1280/900 shape, which
   squashed this ~1:1.1 page flat and put every spotlight in the wrong place. */
.app-shot img{display:block;width:100%;height:auto;aspect-ratio:auto}


/* The top of the Overview, not all of it. The whole page made the card
   1,732px tall at 1440 x 900, two screens of scrolling before the tiles
   (the owner, 2026-10-23: "too long of a scroll"). It now stops at 86%
   of its own width, partway into Profiles, and fades from just above the
   accounts (73% faded too soon, the owner said the same day); /demo has
   the whole thing. cqw so the cut lands in the same place
   in the picture at every width. */
.card-box{container-type:inline-size}
.app-shot{max-height:86cqw}
/* The fade is long and eased, (distance from the foot / 42cqw)^1.5, so the
   card thins out as it goes down. A straight 26cqw ramp read as the card
   being cut off (the owner, 2026-10-23). scene.js's canvas uses the same
   curve: change both together. It reaches nothing 72px above the foot,
   and the link sits in that clear strip: ending at the foot left the last
   faint rows level with the link and past it (the owner, 2026-10-23: "a bit
   of an eye sore").
   */
.card-box .window{--fade:linear-gradient(#000 calc(100% - 42cqw - 72px),rgb(0 0 0 / .65) calc(100% - 31.5cqw - 72px),rgb(0 0 0 / .35) calc(100% - 21cqw - 72px),rgb(0 0 0 / .125) calc(100% - 10.5cqw - 72px),transparent calc(100% - 72px));
  -webkit-mask-image:var(--fade);mask-image:var(--fade)}
.card-box{position:relative}
/* scene.js draws the tilted card here and hides the real one, which keeps
   its place in the page and in the accessibility tree. Once the card is
   flat the canvas must go too: it kept its last tilted frame on top of the
   real card, and a quick scroll left the two showing at once, doubled
   (the owner's screenshot, 2026-10-23). */
.card-box canvas.tilt{position:absolute;top:0;pointer-events:none}
.card-box:not(.drawn) canvas.tilt{visibility:hidden}
.card-box.drawn .window{opacity:0}
/* After the scene, on plain paper: inside it the link sat on the dots. */
/* It sits in the card's clear last 72px, just under where the card is
   gone (the owner, 2026-10-23: dead space, then "a bit up"). */
.card-more{position:relative;z-index:1;margin:-58px 0 0;padding:0;text-align:center;font-size:15px}
.card-more + .band{padding-top:48px}
.card-more a{color:var(--ink);font-weight:600;text-underline-offset:3px}

/* A constant soft shadow avoids repainting the large screenshot during flattening. */
.scene .card-box .window{box-shadow:0 24px 60px -36px rgb(20 24 30 / 22%),0 1px 0 rgb(20 24 30 / 3%)}
.scene + .band,.card-more + .band{border-top:0}

/* ── Features: tall tiles ───────────────────────────────────────────────
   Supabase's pattern: icon and name, then a sentence whose point is in ink
   and the rest grey, and a slice of the app at the foot. */
.tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
.tile{--c-a:var(--accent);--c-g:oklch(68% .14 155);--c-r:oklch(64% .17 25);--c-v:oklch(62% .14 295);--c-y:oklch(78% .13 80);
  background:var(--surface);border:1px solid var(--hairline);border-radius:18px;padding:26px 26px 22px;display:flex;flex-direction:column;gap:10px;min-height:380px;overflow:hidden}
.tile.t-wide{grid-column:span 2}
.tile h3{display:flex;align-items:center;gap:10px;font-size:18px;line-height:1.2;font-weight:650}
.tile h3 > svg{width:20px;height:20px;flex:none;fill:none;stroke:var(--ink);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
/* Plain words, not a pill: "COMPLETE" in a badge read as a task status
   (Astra, as a buyer, 2026-10-01), and the owner found it AI-looking. */
.tile .tier{margin-left:auto;font:500 12.5px/1 var(--site);color:var(--ink-soft);white-space:nowrap}
.tile > p{margin:0;font-size:15px;line-height:1.55;color:var(--ink-soft);max-width:44ch}
.tile > p b{color:var(--ink);font-weight:600}
/* The art is a slice of the app's own screen with the demo vault's rows and
   figures, fading out at the foot, the way Stripe and Linear show product
   (the owner chose it, 2026-10-01). Two rounds of drawn diagrams read as
   AI-made: grey bars standing in for words, floating cards with shadows,
   colour that meant nothing. The figures are the site's demo vault
   (scripts/demo-fixture.mjs) where it has them.

   Motion: tiles.js sets --p on a tile from 0 to 1 as its slice comes up the
   screen, and each part works out its own share with clamp(). With no
   script, or reduced motion, --p stays 1: the finished frame. */
.tile{--p:1}
.tile .art{margin-top:auto;padding-top:20px;margin-bottom:-22px}
.frag{font:13px/1.35 var(--site);color:var(--ink);background:var(--surface);border:1px solid var(--hairline);border-bottom:0;
  border-radius:12px 12px 0 0;padding:12px 14px 16px;font-variant-numeric:tabular-nums;
  -webkit-mask-image:linear-gradient(#000 84%,transparent);mask-image:linear-gradient(#000 84%,transparent)}
.frag table{width:100%;border-collapse:collapse}
.frag td{padding:8px 0;border-top:1px solid var(--hairline);white-space:nowrap;vertical-align:middle}
.frag td + td{padding-left:10px}
.frag td:nth-child(2){white-space:normal}
.frag td.d{color:var(--ink-soft);width:1%}
.frag td.a{text-align:right;font-weight:600;width:1%}
.frag .cat{color:var(--ink-soft);font-size:12px;margin-left:4px}
.frag .fhead{display:flex;justify-content:space-between;gap:10px;margin-bottom:8px}
.frag .fhead span,.frag .k span{color:var(--ink-soft)}
.frag .k{display:flex;justify-content:space-between;margin:10px 0 6px}
.frag .track{height:8px;border-radius:4px;background:var(--paper-raised);position:relative;overflow:hidden}
.frag .track i{position:absolute;inset:0 auto 0 0;border-radius:4px;background:var(--c-g)}
.frag .tag{display:inline-block;font-size:11px;font-weight:600;color:oklch(42% .1 155);background:color-mix(in oklch,var(--c-g) 15%,transparent);border-radius:999px;padding:2px 7px;margin-left:6px}
.frag .note{color:var(--ink-soft);font-size:12px;padding-top:8px;border-top:1px solid var(--hairline)}

/* Ledger: the Groceries filter goes on and the other rows fall back. */
.fbar{display:flex;gap:8px;align-items:center;margin-bottom:8px}
.fbar .search{flex:1;border:1px solid var(--hairline);border-radius:8px;padding:6px 10px;color:var(--ink-soft)}
.fbar .chip{border-radius:999px;padding:4px 10px;font-size:12px;font-weight:600;color:oklch(40% .1 155);
  background:color-mix(in oklch,var(--c-g) 16%,transparent);opacity:clamp(0,calc(var(--p) / .4),1)}
.frag tr.other{opacity:calc(1 - .75 * clamp(0,calc((var(--p) - .3) / .5),1))}

/* CSV import: rows arrive one by one; the one already in Vault greys out. */
.frag tr.in{--q:clamp(0,calc((var(--p) - var(--d)) / .3),1);opacity:var(--q);transform:translateY(calc((1 - var(--q)) * 10px))}
.frag td.st{color:oklch(42% .1 155);font-size:12px;font-weight:600;text-align:right;width:1%;min-width:52px;position:relative}
.frag tr.dup{--s:clamp(0,calc((var(--p) - .8) / .2),1)}
.frag tr.dup td:not(.st){opacity:calc(1 - .55 * var(--s));text-decoration:line-through;text-decoration-color:rgb(0 0 0 / calc(var(--s) * 45%))}
.frag .st-new{opacity:calc(1 - var(--s))}
.frag .st-dup{position:absolute;right:0;top:50%;translate:0 -50%;color:var(--ink-soft);opacity:var(--s)}

/* Receipts: the receipt drops into the row that had none. */
.frag td.rc{width:1%;text-align:center;position:relative}
.frag .thumb{display:inline-block;width:16px;height:20px;border:1px solid var(--hairline);border-radius:3px;vertical-align:middle;
  background:linear-gradient(var(--hairline),var(--hairline)) 3px 5px/9px 1.5px no-repeat,linear-gradient(var(--hairline),var(--hairline)) 3px 9px/7px 1.5px no-repeat,linear-gradient(var(--hairline),var(--hairline)) 3px 13px/9px 1.5px no-repeat,var(--surface)}
.frag .add{display:inline-block;width:16px;height:20px;vertical-align:middle;border:1.5px dashed color-mix(in oklch,var(--ink) 30%,transparent);border-radius:3px;opacity:calc(1 - clamp(0,calc((var(--p) - .6) / .25),1))}
/* left is the cell's padding box: its centre is 5px left of the slot the
   other rows' receipts sit in (td + td's 10px padding), and the receipt
   landed out of line with the one above (2026-10-23). */
.frag .thumb.fly{--q:clamp(0,calc(var(--p) / .85),1);position:absolute;left:calc(50% + 5px);top:50%;margin:-10px 0 0 -8px;
  transform:translate(calc((1 - var(--q)) * -40px),calc((1 - var(--q)) * -70px)) rotate(calc((1 - var(--q)) * -8deg)) scale(calc(1 + (1 - var(--q)) * 2.2));
  box-shadow:0 calc((1 - var(--q)) * 14px) calc((1 - var(--q)) * 24px) rgb(20 24 30 / 14%)}

/* Income breakdown: the month's income fills out into where it went. */
.frag .stack{display:flex;gap:3px;height:14px;margin:4px 0 12px}
.frag .stack i{display:block;border-radius:4px;width:calc(var(--w) * clamp(0,calc(var(--p) / .8),1))}
.frag .s-r{background:var(--c-r)}.frag .s-b{background:oklch(62% .13 245)}.frag .s-y{background:var(--c-y)}.frag .s-g{background:var(--c-g)}
.frag .legend{list-style:none;margin:0;padding:0;display:grid;gap:7px}
.frag .legend li{display:flex;align-items:center;gap:8px}
.frag .legend li i{width:9px;height:9px;border-radius:3px}
.frag .legend b{margin-left:auto;font-weight:600}

/* Rising charges: the dot climbs the price, green to red (tiles.js). */
.frag .big{font-size:22px;font-weight:700;letter-spacing:-.01em}
.frag .big small{font-size:14px;font-weight:500;color:var(--ink-soft)}
.frag .yr{margin:4px 0 10px;color:var(--c-r);font-weight:600;opacity:clamp(0,calc((var(--p) - .75) / .25),1)}
.frag svg{display:block;width:100%;height:auto;overflow:visible}
.frag .creep-ghost{fill:none;stroke:var(--hairline);stroke-width:2;stroke-dasharray:4 5}
.frag .creep-line{fill:none;stroke:url(#pcStroke);stroke-width:2.5;stroke-linecap:round}
.frag .walker{fill:var(--c-r)}
.frag .walker-h{fill:var(--c-r);opacity:.18}
.frag .st-g{stop-color:var(--c-g)}.frag .st-y{stop-color:var(--c-y)}.frag .st-r{stop-color:var(--c-r)}

/* Budget caps: the bars fill; Utilities passes its cap and goes red. */
.frag .cap-row .track i{width:calc(var(--f) * 100% * clamp(0,calc(var(--p) / .8),1))}
.frag .cap-row .track i.red{background:var(--c-r);width:100%;opacity:clamp(0,calc((var(--p) - .8) / .15),1)}

/* Loans: the extra repayment brings the payoff in. */
.frag .track i.grey{background:color-mix(in oklch,var(--ink) 22%,transparent)}
.frag .track i.shrink{width:calc(100% - 13% * clamp(0,calc(var(--p) / .8),1))}
.frag .save{margin:12px 0 0;color:var(--ink-soft);opacity:clamp(0,calc((var(--p) - .7) / .25),1)}
.frag .save b{color:var(--ink);font-weight:600}

/* Investments: the trades and the holding they add up to. */
.frag tr.tot td{font-weight:600}

/* Ownership: a short part of the page after the tiles, not a band of its
   own. Centred on dots under a rule, with three boxed points, it "feels
   like a new page" and the boxes looked "very AI" (the owner, 2026-10-24).
   Now it is set like the rest of the page, on plain paper: the heading and
   line on the left, the three points as plain rows on the right. */
.own-band{--d-ink:var(--ink);--d-soft:var(--ink-soft);--d-line:var(--hairline);
  display:block;background:var(--paper);color:var(--d-ink);padding-block:16px 32px}
/* The gaps either side were the tiles' and Pricing's full 88px and 72px
   on top of this part's own (the owner, 2026-10-24: "tighten the gaps"). */
#features{padding-bottom:56px}
.own-band .wrap{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.1fr);gap:72px;align-items:center}
/* The page's own h2 and lede sizes, like Features and Pricing: at 44px
   and 800 it looked "much bigger than the other stuff" (2026-10-24). */
.db-head h2{color:var(--d-ink)}
.db-head p{color:var(--d-soft);font-size:17px;line-height:1.5;margin:12px 0 0}
.db-head .db-more{margin:20px 0 0;font-size:15px}
.db-more a{color:var(--ink-soft);text-underline-offset:3px}
.db-more a:hover{color:var(--ink)}
/* Plain rows alone were "a little lack lustre" (2026-10-24): the heading
   at the page's section size, and each row its own icon, drawn like the
   feature tiles'. */
.db-points{list-style:none;padding:0;margin:0}
.db-points li{display:flex;align-items:center;gap:16px;padding:18px 0;border-top:1px solid var(--d-line);font-size:15px}
.db-points li:last-child{border-bottom:1px solid var(--d-line)}
.db-points li > svg{flex:none;width:22px;height:22px;fill:none;stroke:var(--ink);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.db-points b{flex:none;width:13em;font-weight:650;color:var(--d-ink)}
.db-points span{color:var(--d-soft)}
/* One row, readable here and scrambled on the server (own.js). Its box
   is a fixed size and both faces share one size and line height: the
   sealed face at 14px moved the row and the caption a few pixels on every
   swap (the owner, 2026-10-24). */
.seal-demo{margin-top:22px;max-width:420px}
.sd-row{display:flex;align-items:center;justify-content:space-between;gap:16px;box-sizing:border-box;height:52px;padding:0 18px;line-height:22px;background:var(--surface);border:1px solid var(--hairline);
  border-radius:12px;font-size:15px;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;
  transition:background-color .4s,border-color .4s}
.sd-d{color:var(--ink)}
.sd-a{font-weight:600}
.seal-demo.sealed .sd-row{background:oklch(96% .015 250);border-color:oklch(88% .035 250)}
.seal-demo.sealed .sd-d,.seal-demo.sealed .sd-a{font-family:ui-monospace,'Cascadia Mono',Consolas,monospace;font-weight:400;color:oklch(52% .05 255);letter-spacing:.02em}
.sd-cap{position:relative;height:20px;line-height:20px;margin-top:8px;padding-left:4px;font-size:13px;color:var(--ink-soft)}
.sd-cap span{position:absolute;left:4px;top:0;transition:opacity .35s}
.sd-there{opacity:0}
.seal-demo.sealed .sd-here{opacity:0}
.seal-demo.sealed .sd-there{opacity:1}
/* own.js: the rows start low, only once script has
   said it will play them, so without it everything simply shows. */
.own-band.armed .db-points li{opacity:0;transform:translateY(10px);transition:opacity .5s,transform .5s cubic-bezier(.2,.7,.2,1)}
.own-band.armed .db-points li:nth-child(2){transition-delay:.1s}
.own-band.armed .db-points li:nth-child(3){transition-delay:.2s}
.own-band.armed.shown .db-points li{opacity:1;transform:none}
.own-band + .band{border-top:0}
#pricing{padding-block:56px}
@media(max-width:900px){
  .own-band .wrap{grid-template-columns:1fr;gap:28px}
  .db-points b{width:auto}
  .db-points li{flex-wrap:wrap;gap:4px 14px}
  .db-points span{flex-basis:100%;padding-left:36px}
}

@media (max-width:1000px){
  .tiles{grid-template-columns:1fr 1fr}
}
@media (max-width:620px){
  .tiles{grid-template-columns:1fr}
  .tile.t-wide{grid-column:auto}
  .tile{min-height:0}
}

.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* No sticky hold: the document keeps moving while the angle resolves. */
.scene.live .card-box{position:relative;top:auto!important}
.scene.live .hold,.hold{display:none!important}
.hero-actions{display:flex;gap:12px;margin-top:26px}
/* Opaque white keeps the dot field behind the Pricing action. */
.hero-actions .btn-quiet{background:var(--surface)}
.hero-actions .btn-quiet:hover{background:var(--surface);border-color:var(--ink)}
.frag .stack i{width:var(--w)}
.tile[data-anim="income"] .stack i,.tile[data-anim="income"] .legend i{background:color-mix(in oklch,var(--segment) calc(var(--p)*100%),#dededb)}
.tile[data-anim="income"] .s-r{--segment:var(--c-r)}
.tile[data-anim="income"] .s-b{--segment:oklch(62% .13 245)}
.tile[data-anim="income"] .s-y{--segment:var(--c-y)}
.tile[data-anim="income"] .s-g{--segment:var(--c-g)}
.creep-area{opacity:var(--p)}
.plans{align-items:stretch}
/* White like every other card, picked out by its border rather than a grey
   fill: the grey read as a disabled card from another palette (2026-10-23). */
.plan{border-radius:18px}
.plan[data-plan="complete"]{--phone-rank:0;border-color:var(--ink);box-shadow:0 0 0 1px var(--ink)}
.plan[data-plan="connect"]{--phone-rank:1}
.plan[data-plan="free"]{--phone-rank:2}
.plan .includes{margin-top:auto}
.plan .ticks{min-height:310px}
.compare-link{display:inline-block;margin-top:26px;font-size:15px;color:var(--ink-soft);text-decoration:underline;text-underline-offset:4px}
.compare-page{padding-block:60px 90px}
.comparison-head,.comparison-row{display:grid;grid-template-columns:1.5fr repeat(3,minmax(0,1fr));gap:20px;align-items:center;text-align:center}
.comparison-head{padding:36px 0;position:sticky;top:0;background:var(--paper);z-index:2;border-bottom:1px solid var(--hairline)}
.comparison-head h2{font-size:24px}
.comparison-head .price{font-size:34px;font-weight:700;display:block}
.comparison-head small{display:block;font-size:13px;font-weight:400}
.comparison-row{padding:28px 0;border-bottom:1px solid var(--hairline)}
.comparison-row h3{text-align:left;font-size:16px;font-weight:500}
.missing{font-size:32px;line-height:1;color:var(--ink-soft)}
@media(max-width:620px){
 .card-box{margin-top:30px;perspective:none}
 .card-box .window{transform:none!important;box-shadow:none}
 .app-shot{max-height:480px;overflow:hidden}
 .app-shot img{width:760px;max-width:none;object-position:left top}
 .scene{padding-bottom:40px}
 .plan .ticks{min-height:0}
 .comparison-head,.comparison-row{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
 .comparison-head>div:first-child{display:none}
 .comparison-head h2{font-size:17px}
 .comparison-head .price{font-size:25px}
 .comparison-row h3{grid-column:1/-1;text-align:center;margin-bottom:12px}
 .comparison-row>div{font-size:13px}
}
/* Small screens get a straight, fitted Overview and a readable figures summary. */
@media(max-width:620px){.app-shot img{width:100%;max-width:100%}.app-shot{max-height:440px}}

/* Keep the detailed comparison beside the pricing introduction, before the cards. */
.pricing-head{max-width:none;display:flex;align-items:flex-start;justify-content:space-between;gap:24px}
.pricing-head p{margin-top:12px}
.pricing-compare{flex-shrink:0;margin-top:8px;font-size:15px;color:#656970;text-decoration:none}
.pricing-compare:hover{text-decoration:underline}
@media(max-width:620px){.pricing-head{flex-wrap:wrap;gap:12px}.pricing-compare{margin-top:0;margin-left:auto}}

/* Phones lead straight into features instead of showing a miniature desktop app. */
@media(max-width:620px){.scene .card-box,.card-more,.scene .metrics-summary{display:none}.plans>.plan{order:var(--phone-rank)}}
/* Three panels need about 1250px; narrower, they stack. */
@media(max-width:1250px){
}
