/* ============================================================================
   Global Office: Sync, the marketing website.

   The look is the app's look on purpose, so the tokens below are lifted from
   the app's own stylesheet rather than invented:
   the same near black ground, the same Inter and Roboto Mono, the same ledger
   accent hues. Somebody who arrives here and then opens the app should not feel
   they changed products.

   The fonts are served from this site rather than from Google, so the content
   security policy needs no external font host.

   site/js/zoom.js opens a screenshot at a size worth reading. It is a file rather
   than an inline script because the policy on this site is script-src 'self' with no
   'unsafe-inline', which is the whole reason the policy is worth having.

   HOUSE RULES THAT APPLY TO EVERY LINE OF COPY ON THIS SITE:
     - No em dashes, anywhere a person reads. Comma, colon, brackets, or two
       sentences.
     - American spelling. Labor, color, categorize, gray.
     - Never "receipt scanner". Never name Wave. Never name W-2 in copy.
     - Never imply the AI files anything by itself: it proposes, you approve.
   ========================================================================== */

/* TWO FILES, WHICH IS WHAT DESIGN_RULES.md SECTION 5 ALLOWS, and it used to be seven.
   Five weights of Inter and two of Roboto Mono as TrueType came to 1.7MB; these two variable
   woff2 faces, subset to latin, come to 124KB and cover every weight either one is used at.

   Both are SIL Open Font License 1.1, checked against google/fonts rather than assumed, so
   self hosting and shipping the file alongside the site is what the license is for. They are
   served from here rather than from Google because this site's policy is font-src 'self'.

   Roboto Mono is gone rather than replaced. It was only ever used at 10 to 13px for eyebrows
   and small labels, where the machine's own monospace face reads at least as well and costs
   nothing, and keeping it would have meant three files to say what two can. */
@font-face {
  font-family:'Inter';
  src:url('../fonts/inter.woff2') format('woff2-variations'), url('../fonts/inter.woff2') format('woff2');
  font-weight:300 700; font-style:normal; font-display:swap;
}
@font-face {
  font-family:'Bricolage Grotesque';
  src:url('../fonts/bricolage-grotesque.woff2') format('woff2-variations'), url('../fonts/bricolage-grotesque.woff2') format('woff2');
  font-weight:400 800; font-style:normal; font-display:swap;
}

/* ============================================================================
   EVERY COLOR, TYPE AND SPACING VALUE IN THE SITE LIVES HERE.

   DESIGN_RULES.md section 5: "A raw hex or px value outside :root is a bug."
   Enforced by tools/check_design_rules.js rather than by anybody remembering.

   The palette is the app's, by Steve's instruction: "use that from the app. The
   site is for the app, so it should be similar." Checked against the app's own
   src/style-v2-src.css rather than assumed. The ground, the alt band, the muted
   text and all eight ledger hues agree with it exactly.
   ========================================================================== */
:root{
  /* ---- ground and paper, straight from the app's :root ---- */
  --bg:#0A090E;
  --bg-alt:#0A0C10;
  --card:#0E1117;
  --card-2:#12161E;
  --card-lift:#101821;      /* a card that is being singled out */
  --ink-on-accent:#031009;  /* the only text color that sits ON the accent */
  --shadow:#000000;

  /* ---- rules and edges ---- */
  --line:#1E2530;
  --line-soft:#171C25;
  --line-lit:#2C3644;       /* a rule that is being hovered or focused */
  --line-warm:#2A3A44;      /* the edge of a singled-out card */
  --line-mid:#33404F;
  --scroll-thumb:#2A3341;

  /* ---- text ---- */
  --text:#FFFFFF;
  --lede:#D3DBE6;
  --body:#C6CFDB;
  --muted:#94A3B8;
  --dim:#6B7889;
  --faint:#4C5867;          /* a deliberate absence, as in a comparison table */
  --ink-on-taxdocs:#1A1400;
  /* ---- each edition's own color, sampled off its logo in public/ (2026-09-14) ---- */
  --ed-full:#10B981;        /* SYNC in the wordmark */
  --ed-contractor:#BF021D;  /* the CONTRACTORS EDITION stamp */
  --ed-solo:#F9C31F;        /* the Solo Edition line */
  --ed-full-hover:#34CD9A;
  --ed-contractor-hover:#D61A34;
  --ed-solo-hover:#FBD155;
  --paper:#ECEFF3;          /* Tailored Light's own ground, only on the button that names it */
  --paper-ink:#0E1A2B;      /* and its ink */

  /* ---- the ledger accents, hue for hue with the app ---- */
  --receipts:hsl(345,80%,60%);
  --income:hsl(150,70%,55%);
  --utilities:hsl(25,90%,55%);
  --mileage:hsl(180,80%,50%);
  --taxdocs:hsl(45,95%,55%);
  --compliance:hsl(220,85%,60%);
  --quotes:hsl(270,80%,65%);
  --donations:hsl(270,80%,65%);   /* the app's purple, which Donations shares with Quotes */
  --red:hsl(0,85%,60%);

  /* THE ONE ACCENT, per section 0. Actions only: buttons, links, focus rings.
     Never a divider, never an icon fill, never a background wash. The seven
     above are not decoration either; each one NAMES a ledger and is used only
     where that ledger is what the reader is looking at. */
  --accent:var(--income);

  /* ---- spacing, section 0's scale. 8px base ---- */
  --s-1:4px;
  --s-2:8px;
  --s-3:16px;
  --s-4:24px;
  --s-5:40px;
  --s-6:64px;
  --s-7:96px;

  /* THE ONE TRANSITION DURATION. Section 2: "Transitions, where used, are short
     (<=150ms) and apply to colour only." Naming it once is what makes that true by
     construction, and it is also what lets prefers-reduced-motion turn every
     transition on the site off by redefining a single value, with no !important and
     no specificity fight. Section 5 bans !important; section 6 requires reduced
     motion to be respected. This is how both are satisfied at once. */
  --t:150ms;

  --maxw:1120px;
  --radius:14px;

  /* Roboto Mono is gone; this is the machine's own monospace face and it costs
     no file. It is used at 10 to 13px for eyebrows and labels and nowhere else. */
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
  --sans:'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  /* Section 0's display face, Steve's pick, taken from the header type on
     features-overview.html. Headings only, never body copy. */
  --display:'Bricolage Grotesque','Inter',-apple-system,BlinkMacSystemFont,sans-serif;
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
/* CLIP, NOT HIDDEN. `hidden` makes the body a scroll box of its own, and a sticky element
   sticks to its nearest scroll box: the header was written sticky from the first day and
   scrolled away with the page in every browser, because the box it was sticking to never
   scrolls. `clip` cuts the sideways overflow the same way without making a scroll box.
   `hidden` stays first for any browser too old to know `clip`. Measured, 2026-09-12. */
html,body{max-width:100%;overflow-x:hidden;overflow-x:clip}
body{
  margin:0;
  background:var(--bg);
  color:var(--body);
  font-family:var(--sans);
  font-weight:300;
  font-size:16.5px;
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img{max-width:100%;height:auto;display:block}
a{color:inherit}

.wrap{max-width:var(--maxw);margin:0 auto;padding:0 24px}
.narrow{max-width:760px}

/* ---------------------------------------------------------------- headings */
/* SECTION 0'S DISPLAY FACE, ON HEADINGS AND NOWHERE ELSE. Bricolage Grotesque is
   Steve's pick, taken from the header type on features-overview.html. It is a variable
   face with an optical size axis, so the size it is set at is fed to it and the letter
   shapes change with it: tighter and more compact large, looser and more open small.
   That is the thing a static face cannot do and it is most of why this reads as drawn
   for the size rather than scaled to it.

   Section 2: "Don't build hierarchy out of size alone. Use weight, colour, spacing,
   rules, and case." The change of FACE between headings and body is part of that
   hierarchy too, and it is doing work that another four pixels of font-size would not. */
h1,h2,h3,h4{
  font-family:var(--display);
  font-variation-settings:'opsz' 40;
  color:var(--text);font-weight:700;letter-spacing:-.022em;line-height:1.08;margin:0;
}
h1{font-variation-settings:'opsz' 96}
h2{font-variation-settings:'opsz' 64}
h3,h4{font-variation-settings:'opsz' 24}
h1{font-size:clamp(34px,6.2vw,62px)}
h2{font-size:clamp(26px,3.8vw,40px);letter-spacing:-.02em;line-height:1.12}
h3{font-size:clamp(18px,2.2vw,21px);letter-spacing:-.012em;line-height:1.25;font-weight:600}
p{margin:0 0 16px}
p:last-child{margin-bottom:0}
strong,b{color:var(--text);font-weight:600}

.eyebrow{
  font-family:var(--mono);
  font-size:11px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--dim);margin:0 0 18px;font-weight:400;
}
/* A HYPHEN IN TRACKED-OUT CAPITALS READS AS A LONG DASH, and Steve will not have one on the site:
   "Absolutely NO em dashes!!!!" The character is and always was a plain hyphen; .2em of tracking on
   each side is what made it look like one, so the hyphen keeps none of it. */
.eyebrow .hy{
  /* The mono face draws a hyphen almost as wide as its cell, which is what makes it read as a
     dash in tracked-out capitals. The text face's hyphen is short, so the hyphen alone is set in
     it, at the same size, and the tracking around it is taken off. */
  /* The tracking sits AFTER each letter, so the E left a gap before the hyphen and the hyphen's
     own zero tracking left none after it: a space on one side only. Both sides are closed up now,
     which is the "neither" of Steve's "both or neither". */
  font-family:var(--sans);letter-spacing:0;margin:0 0 0 -.2em;
}
.lede{font-size:clamp(17px,2.2vw,19.5px);line-height:1.55;color:var(--lede);font-weight:300}

/* ------------------------------------------------------------------ header */
.site-head{
  position:sticky;top:0;z-index:50;
  /* SOLID, NOT FROSTED. Section 2 bans backdrop-filter blur, and section 2's own
     advice for separating regions is "a 1px rule or a background-colour change",
     which is exactly what a sticky header needs anyway. */
  background:var(--bg);
  border-bottom:1px solid var(--line);
}
.site-head .wrap{display:flex;align-items:center;gap:22px;height:64px;position:relative}
.brand{display:flex;align-items:center;text-decoration:none;flex:0 0 auto}
.brand img{height:23px;width:auto}
.nav{display:flex;align-items:center;gap:24px;margin-left:auto}
.nav a{
  text-decoration:none;color:var(--muted);font-size:14.5px;font-weight:400;
  transition:color var(--t) ease;
}
.nav a:hover,.nav a[aria-current="page"]{color:var(--text)}
/* THE BUY BUTTON'S LABEL WAS INVISIBLE, and it is the one control the whole site is
   pointing at. `.nav a` is two classes' worth of specificity and `.btn-primary` is one,
   so the muted grey below won every time: near black text became grey text on a bright
   green button. Measured in a real browser, not guessed: the computed color came
   back rgb(148,163,184). Anything that puts a button inside the nav needs this. */
.nav a.btn-primary{color:var(--ink-on-accent)}
.nav a.btn-ghost{color:var(--text)}
.nav .sep{width:1px;height:20px;background:var(--line);flex:0 0 auto}
.nav-links{display:flex;align-items:center;gap:24px}

/* ------------------------------------------------------------ the phone menu */
/* Steve, 2026-09-12: on a phone the header was too crowded and two links had been dropped to
   make it fit. So on a phone the links live behind a hamburger, beside the logo, and "Buy it"
   stays on the right. On the front page they start out laid along the top and fold into the
   hamburger as the wordmark flies in (js/logo-dock.js); every other page opens folded. */
.menu{display:none}
.menu summary{
  list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:center;
  width:36px;height:36px;margin-left:-8px;border-radius:8px;
}
.menu summary::-webkit-details-marker{display:none}
.menu summary:focus-visible{outline:2px solid var(--income);outline-offset:2px}
.burger,.burger::before,.burger::after{
  display:block;width:20px;height:2px;border-radius:1px;background:var(--text);
  transition:transform var(--t) ease,background var(--t) ease;
}
.burger{position:relative}
.burger::before,.burger::after{content:"";position:absolute;left:0}
.burger::before{top:-6px}
.burger::after{top:6px}
.menu[open] .burger{background:transparent}
.menu[open] .burger::before{top:0;transform:rotate(45deg)}
.menu[open] .burger::after{top:0;transform:rotate(-45deg)}
.menu-panel{
  position:absolute;left:0;right:0;top:100%;z-index:1;
  display:flex;flex-direction:column;padding:4px 16px 12px;
  background:var(--bg);border-bottom:1px solid var(--line);
}
.menu-panel a{
  padding:13px 0;font-size:17px;color:var(--text);text-decoration:none;
  border-bottom:1px solid var(--line-soft);
}
.menu-panel a:last-child{border-bottom:0}
.menu-panel a[aria-current="page"]{color:var(--accent)}
/* The head is one flex row, and at 390px nothing in it was allowed to give, so the row
   overflowed and took the last item with it. The brand is what yields: it shrinks, and
   the nav never does. */
@media (max-width:800px){
  .site-head .wrap{height:58px;gap:12px;padding:0 16px}
  .brand{min-width:0;overflow:hidden}
  .brand img{height:20px}
  .nav{gap:16px;flex:0 0 auto}
  .nav .hide-sm{display:none}
  .nav a{font-size:13.5px}
  .menu{display:block;flex:0 0 auto}
  .nav-links{display:none}

  /* THE FOLD, front page only. At the top of the page the links lie along the left of the
     header, where the hamburger and the logo will be. --dock runs from 0 to 1 as the wordmark
     flies in, and the row of links shrinks toward the hamburger over that WHOLE run, so it ends
     the moment the logo lands; --ox is where the hamburger's bars start, measured by
     js/logo-dock.js. The whole row scales as one, so the links never slide into each other.

     ONE MOTION, at Steve's word, 2026-09-12. The first version folded the links in the first
     half of the run and faded the hamburger in from the middle, and on his phone the hamburger
     was already there while the logo was still below the bar. The logo only enters the bar at
     about 0.6 of the run, so that is where the hamburger starts to appear, and the links are
     gone just before the logo lands. Only transforms and opacity change, never layout, so the
     logo's landing spot is where it always was. */
  .logo-docking .nav-links{
    display:flex;position:absolute;left:16px;top:0;height:100%;gap:14px;
    transform-origin:var(--ox,0px) 50%;
    transform:scale(calc(1 - var(--dock,0) * .92));
    opacity:clamp(0, calc(1 - (var(--dock,0) - .45) * 2.2), 1);
  }
  .logo-docking .menu{opacity:clamp(0, calc((var(--dock,0) - .6) * 2.5), 1)}
  .logo-docking:not(.logo-half) .menu{visibility:hidden}
  .logo-half .nav-links{pointer-events:none}
  .logo-docked .nav-links{visibility:hidden}
}
@media (max-width:420px){
  .site-head .wrap{gap:10px}
  .logo-docking .nav-links{gap:11px}
  .brand img{height:16px}
  .nav{gap:12px}
  .nav a{font-size:12.5px}
  .btn-sm{padding:8px 12px;font-size:13px}
}

/* ------------------------------------------------------------------ button */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  font-family:var(--sans);font-size:15px;font-weight:600;letter-spacing:-.005em;
  text-decoration:none;cursor:pointer;
  padding:12px 22px;border-radius:10px;
  border:1px solid transparent;
  transition:background var(--t) ease, border-color var(--t) ease;
}
.btn:active{transform:translateY(1px)}
/* ONE FLAT COLOR, no gradient and no glow under it. DESIGN_RULES.md section 2, and
   section 0 makes this the site's single accent: it appears on actions and nowhere
   else. The hover is a color change, which is also the only hover section 2 allows. */
.btn-primary{
  background:var(--accent);
  color:var(--ink-on-accent);
}
.btn-primary:hover{background:hsl(150,70%,62%)}
.btn-ghost{background:transparent;border-color:var(--line);color:var(--text)}
.btn-ghost:hover{border-color:var(--line-lit);background:rgba(255,255,255,.03)}
.btn-sm{padding:9px 16px;font-size:14px;border-radius:9px}
.btn-row{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
/* TWO BUTTONS STAY SIDE BY SIDE ON A PHONE, at Steve's word, 2026-09-12. They used to wrap to
   one per row. A row of two no longer wraps; each button takes the width of its own words and a
   little less padding, and the words never break inside a button. Only rows of exactly two:
   the rows of three (the editions page's opening, the 404) would not fit one line on a phone,
   so they still wrap. A browser without :has() wraps as before, which is still correct. */
@media (max-width:560px){
  .btn-row:not(:has(> :nth-child(3))){flex-wrap:nowrap;gap:10px}
  .btn-row:not(:has(> :nth-child(3))) > .btn{
    flex:0 1 auto;min-width:0;white-space:nowrap;padding:12px 16px;font-size:14.5px;
  }
}

/* ------------------------------------------------------------------- bands */
.band{padding:76px 0;border-top:1px solid var(--line-soft)}
.band.tight{padding:56px 0}
.band.alt{background:var(--bg-alt)}
.band-head{max-width:720px;margin:0 0 40px}
.band-head p{margin-top:14px}
@media (max-width:800px){ .band{padding:54px 0} .band-head{margin-bottom:30px} }

/* -------------------------------------------------------------------- hero */
.hero{
  /* 40px more below than the scale's 64, at Steve's ask, so the hero has room under it
     now that the screenshot has moved out of it and down into the section that argues
     for it. Written as the two scale steps it is made of rather than as 104. */
  position:relative;padding:var(--s-7) 0 calc(var(--s-6) + var(--s-5));overflow:hidden;
  /* NO GLOW. Section 2 names this one directly: "No dark hero with a blurred coloured
     glow behind the headline." Two radial washes in mint and blue were sitting here and
     they are gone. What separates the hero from the band under it now is a rule and a
     change of ground, which is what section 2 asks for everywhere else too. */
  background:var(--bg);
  border-bottom:1px solid var(--line-soft);
}
.hero::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background-image:linear-gradient(rgba(255,255,255,.016) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(255,255,255,.016) 1px,transparent 1px);
  background-size:64px 64px;
  mask-image:radial-gradient(760px 460px at 50% 0%,var(--shadow),transparent 78%);
  -webkit-mask-image:radial-gradient(760px 460px at 50% 0%,var(--shadow),transparent 78%);
}
.hero .wrap{position:relative;z-index:1}
.hero h1{margin-bottom:20px}
.hero .three-lines{
  font-family:var(--mono);font-size:12.5px;letter-spacing:.02em;color:var(--muted);
  margin:0 0 22px;line-height:1.9;
}
.hero .three-lines b{color:var(--income);font-weight:500}
.hero .lede{max-width:60ch;margin-bottom:22px}
/* The second paragraph is the turn, and it is the reason anybody scrolls. It gets a little
   more weight than the mechanism above it without becoming a pull quote, because a pull
   quote here would read as an ad about itself. */
.hero .lede.turn{color:var(--text);margin-bottom:30px}
.hero .lede.turn::first-line{color:var(--text)}
.hero .btn-row{margin-bottom:14px}
.hero .fineprint{font-size:13.5px;color:var(--dim);margin:0}
@media (max-width:800px){ .hero{padding:56px 0 48px} }

/* THE LOGO THAT FLIES INTO THE HEADER. Steve, 2026-09-12: the page opens with the wordmark big
   and centered above the hero, and as it scrolls the wordmark travels up into the header,
   shrinking as it goes: the app's own opening, driven by the scroll instead of a timer.
   js/logo-dock.js does the moving. Without it (no script, or reduced motion asked for) the big
   mark simply stands here and the header keeps its own small one, so nothing is lost. */
/* 50px more under it than the scale gives, at Steve's word, 2026-09-12. */
.hero-mark{text-align:center;margin-bottom:calc(var(--s-6) + 50px)}
.hero-mark img{display:inline-block;width:min(100%,820px);height:auto;vertical-align:top}
@media (max-width:800px){ .hero-mark{margin-bottom:calc(var(--s-5) + 50px)} }
/* While the script runs, the in-page mark only holds its space and the header's own logo stays
   hidden until the flying one lands on it; the two are never seen at once. */
.logo-docking .hero-mark img{visibility:hidden}
.logo-docking:not(.logo-docked) .site-head .brand img{visibility:hidden}
.logo-fly{
  position:fixed;left:0;top:0;z-index:60;pointer-events:none;
  transform-origin:0 0;will-change:transform;height:auto;
}
.logo-docked .logo-fly{visibility:hidden}

/* ---------------------------------------------------------------- contents */
/* Modeled on the contents at the head of the app's features page, and set tighter, at
   Steve's ask, 2026-09-12: three columns of short entries instead of wide ones that wrap. */
nav.toc{background:var(--bg-alt);border-bottom:1px solid var(--line-soft);padding:var(--s-5) 0}
nav.toc .eyebrow{margin-bottom:var(--s-3)}
/* Numbered DOWN each column, then across, at Steve's ask: 1 to 5 in the first column, 6 to 10
   in the second. That is what columns do and a grid does not. */
.toclist{
  list-style:none;margin:0;padding:0;counter-reset:toc;
  columns:4;column-gap:var(--s-4);
}
.toclist li{counter-increment:toc;break-inside:avoid}
.toclist a{
  display:flex;align-items:baseline;gap:10px;padding:4px 0;
  color:var(--muted);text-decoration:none;font-size:14.5px;line-height:1.35;
  transition:color var(--t) ease;
}
.toclist a::before{
  content:counter(toc);font-family:var(--mono);font-size:10.5px;color:var(--dim);min-width:16px;
}
.toclist a:hover{color:var(--text)}
@media (max-width:980px){ .toclist{columns:3} }
@media (max-width:660px){ .toclist{columns:2;column-gap:var(--s-3)} .toclist a{font-size:14px} }
@media (max-width:420px){ .toclist{columns:1} .toclist a{padding:6px 0} }

/* The way back to the contents, at the foot of every section. It says "Back to contents"
   rather than "Top" because it stops at the list, not at the top of the page. */
.backtoc{display:flex;justify-content:flex-end;margin:var(--s-5) 0 0}
.backtoc a{
  display:inline-flex;align-items:center;gap:10px;padding:6px 0;
  font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim);text-decoration:none;transition:color var(--t) ease;
}
.backtoc a::before{content:"";width:22px;height:2px;background:var(--line);transition:background var(--t) ease}
.backtoc a:hover{color:var(--text)}
.backtoc a:hover::before{background:var(--accent)}
/* Between two picture rows (the Centers), the link sits in the 112px that already separates
   them rather than adding to it: the row above keeps its 40, and the 46 below it plus the
   link's own height make up the rest. */
.split + .backtoc{margin-top:0}
.backtoc + .split{margin-top:46px}

/* --------------------------------------------------------------- shot / figure */
figure{margin:0;min-width:0;max-width:100%}
.shot,.soon-frame{
  border:1px solid var(--line);border-radius:var(--radius);
  background:var(--card);overflow:hidden;
  box-shadow:0 30px 70px -34px rgba(0,0,0,.9), 0 0 0 1px rgba(255,255,255,.02) inset;
  cursor:zoom-in;position:relative;
  transition:border-color var(--t) ease;
}
.shot:hover,.shot:focus-visible{border-color:var(--line-mid)}
.shot:focus-visible{outline:2px solid var(--income);outline-offset:3px}
/* A small hint that the picture opens, drawn rather than written, so it costs no
   line of copy and does not repeat itself down the page. */
.shot::after{
  content:"";position:absolute;right:12px;top:12px;
  width:30px;height:30px;border-radius:8px;pointer-events:none;
  background:rgba(10,9,14,.72);border:1px solid rgba(255,255,255,.14);
  background-image:
    linear-gradient(to right, rgba(255,255,255,.85) 2px, transparent 2px),
    linear-gradient(to bottom, rgba(255,255,255,.85) 2px, transparent 2px);
  background-size:11px 2px, 2px 11px;
  background-position:center, center;
  background-repeat:no-repeat;
  opacity:0;transition:opacity var(--t) ease;
}
.shot:hover::after,.shot:focus-visible::after{opacity:1}
@media (hover:none){ .shot::after{opacity:.85} }
.shot img{width:100%}

/* SOLO IS NOT BUILT, and its picture says so. Steve, 2026-09-12: blurred, dimmed, and "Coming
   Soon" on it. The same frame as every other screenshot but not one that opens: enlarging it would
   show the sharp picture the blur is there to hold back, so it is not a .shot and the zoom viewer
   never sees it. The words are the display face in Solo's own yellow, the color of the line under
   its logo, with the edition named small above them the way an eyebrow names a section. The
   picture is scaled up a touch so the blur has no soft light edge showing inside the frame. */
.soon-frame{cursor:default;position:relative}
.soon-frame img{
  display:block;width:100%;
  filter:blur(6px) saturate(.85) brightness(.66);
  transform:scale(1.06);
}
.soon-label{
  position:absolute;inset:0;margin:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;
  text-align:center;
}
.soon-label span{
  font-family:var(--mono);font-size:11px;letter-spacing:.24em;text-transform:uppercase;
  color:var(--muted);
}
.soon-label b{
  font-family:var(--display);font-weight:700;font-variation-settings:'opsz' 96;
  font-size:clamp(32px,5.4vw,58px);line-height:1;letter-spacing:-.025em;
  color:var(--taxdocs);text-shadow:0 2px 30px var(--shadow);
}
figcaption{
  /* The same color as the body text in the numbered steps, at Steve's ask: it was
     --dim and read as an afterthought under pictures that are carrying the argument.
     The SIZE stays smaller than that body text on purpose, because a caption that
     matches its paragraph in both weight and size stops reading as a caption. */
  font-size:13.5px;color:var(--muted);margin-top:12px;line-height:1.5;
  font-weight:300;
}

/* A DESKTOP SCREENSHOT SCALED TO 390px IS AN IMPRESSION OF DENSITY, NOT A PICTURE OF
   ANYTHING, AND THE ANSWER TO THAT CHANGED. It used to be that the frame scrolled
   sideways on a phone and the image kept a readable size inside it. Now the picture
   opens, so the phone shows it whole and a tap gives you the real thing, full size and
   pannable. That is better on two counts: nothing has to be discovered by swiping a
   thing that does not look swipeable, and a drag to pan no longer competes with a tap
   to open. The sideways scroll survives only where it is still the right answer, which
   is the comparison table on the editions page. */
@media (max-width:760px){
  .shot{min-width:0;max-width:100%}
  .swipe-hint{
    display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;
    text-transform:uppercase;color:var(--dim);margin-top:9px;
  }
}
.swipe-hint{display:none}
/* The Information Center screenshot. It used to sit under the hero and be called
   .hero-shot; it lives in the section about the ledgers talking to each other now,
   between the last of the examples and the line saying none of it was typed in, which
   is the claim the picture actually makes. */
.talk-shot{margin-top:calc(var(--s-5) + var(--s-2))}
@media (max-width:800px){ .talk-shot{margin-top:var(--s-4)} }

/* ------------------------------------------------------------- split rows */
/* min-width:0 IS LOAD BEARING ON EVERY ONE OF THESE. A grid item defaults to
   min-width:auto, which means it refuses to shrink below its content: the 680px wide
   screenshot inside .shot stretched its whole column to 706px on a 390px phone, and
   `overflow-x:hidden` on the body then CLIPPED the overflow rather than revealing it.
   Everything past 390px was simply gone, and a check asking
   document.documentElement.scrollWidth reported no horizontal scroll, because there
   was none: the page was cut off instead. Measure body.scrollWidth and element rects. */
/* 40px of air above and below every picture row, at Steve's word, 2026-09-10.
   Adjacent margins collapse to the larger, so the 72px that already separated two
   rows carries the 40 above rather than adding to it: between-row spacing is written
   out as 112 so the 40 is actually there. */
/* 40px more under the bank-feed paragraph before the first picture row, at Steve's
   word. The 40px that .band-head already carried was being collapsed against the
   40px on .split below, so the gap was 40 and not 80; this states the whole 80. */
#contractors .band-head{margin-bottom:80px}
/* 30px further down for the lapsed-insurance row and its picture, at Steve's word.
   The band head's own 40 was collapsing with the 40 on the .split below it, so the
   whole 70 is stated here. */
#missing .band-head{margin-bottom:70px}
/* 50px of breathing room between the 1099 row and the box under it, at Steve's word.
   .apart carries 40 and the row above carries 40; they collapse to 40, so the whole
   90 is stated here. */
#missing .split + .notice.apart{margin-top:150px}
/* 30px of breathing room under "Both editions send to all three." before the first picture row,
   at Steve's word 2026-09-17. Same collapse as the two above: .band-head's own 40 collapses
   against the 40 on .split, so the whole 70 is stated here rather than added to. */
#export .band-head{margin-bottom:70px}
.split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:52px;align-items:center;margin:40px 0}
.split > *{min-width:0}
.split + .split{margin-top:112px}
/* 40px of air under the LAST picture row, where the two cards follow it, at Steve's
   word. The row's own 40 below collapses into the 64 .break already carries, so the
   whole figure is stated here rather than added to. 40px, then another 30. Scoped to a picture row followed
   by cards so the other .break uses are untouched. */
.split + .cards.break{margin-top:134px}

/* A CARD THAT ALREADY HAS ITS OWN FRAME. The alert cutout carries the app's own red
   border and rounded corners in its alpha channel, so it gets no .shot around it;
   drop-shadow rather than box-shadow, because box-shadow would draw the shadow of the
   rectangle the picture sits in and this picture's corners are transparent. */
.card-shot{margin:0}
/* The headline sits over the picture in the left column, with the paragraphs beside
   them both, at Steve's word. Both columns start at the top rather than centring on
   each other, or the headline would float away from the picture it belongs to. */
/* THE HEADLINE SITS OVER THE PICTURE, AND THE PARAGRAPHS CENTRE ON THE PICTURE RATHER
   THAN ON THE COLUMN. Steve asked for the headline above the picture on the left and
   the picture's middle level with the middle of the paragraph text. Those two cannot
   both come out of one row: the headline is 164px tall and the picture 283, so putting
   the picture's centre at the paragraphs' centre would have run it up through the
   headline. So the left column is two rows, the picture and the paragraphs share the
   second one, and both centre in it. Two things centred in the same row have the same
   middle by construction, whichever of them is taller, at any width. */
.asking-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);column-gap:52px}
.asking-grid > *{min-width:0}
.asking-head{grid-column:1;grid-row:1}
.asking-grid .card-shot{grid-column:1;grid-row:2;align-self:center}
.asking-copy{grid-column:2;grid-row:2;align-self:center}
.asking-head .eyebrow{margin-bottom:12px}
@media (max-width:900px){
  .asking-grid{grid-template-columns:1fr;row-gap:26px}
  .asking-head,.asking-grid .card-shot,.asking-copy{grid-column:1;grid-row:auto}
}
.card-shot img{width:100%;height:auto;filter:drop-shadow(0 26px 54px rgba(0,0,0,.85))}
/* 15% off the 1099 card, at Steve's word. It is a card rather than a screenshot, so
   at full column width it was reading larger than the pictures around it. Its own
   text is 633px wide natively and it is shown at about 434, which is still more
   pixels than the screen it was taken from. */
.card-shot.reduced img{width:85%}

/* THREE PHONES, SIDE BY SIDE. They are cutouts with real alpha, so the shadow has to follow
   the phone's own rounded outline: box-shadow would draw the shadow of the rectangle the
   picture sits in, with corners the phone does not have. Same reason as the hero. */
.phones{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--s-5);
         margin-top:var(--s-5); align-items:end }
.phones > *{ min-width:0; margin:0 }
.phones img{ width:100%; height:auto; display:block;
             filter: drop-shadow(0 10px 18px rgba(0,0,0,.55)) drop-shadow(0 44px 70px rgba(0,0,0,.75)) }
.phones figcaption{ text-align:center }

/* A DETAIL PROJECTING OUT OF ITS OWN SCREENSHOT. The Audit Trail is a wide table and the panel
   that carries the whole point of the section is a narrow column on the right of it; at the
   width of a column on this page that panel is unreadable.
   The first attempt outlined the panel on the screenshot and put a copy beside it. Steve: *"it's
   a little confusing. It's two pictures stacked on top of one another. It needs to look like the
   audit information is projecting out of the underlying picture."* He was right: two equally
   sharp, equally bright pictures at the same distance read as two pictures. So the screen behind
   is dimmed and pushed slightly out of focus, the panel comes forward at half again the size it
   was, and the green edge moved off the screenshot and onto the panel, where it marks the thing
   being looked at rather than the place it came from. */
.magnify{ position:relative; margin:0; padding:56px 0 }

/* NOT backdrop-filter. That is glassmorphism, which section 2 bans, and it is a different thing:
   this blurs the picture itself to put it behind, rather than blurring whatever happens to be
   under a translucent card. Faint on purpose. Enough to sit back, not enough to look broken, and
   the picture is still its own unblurred self when it opens. */
.magnify .shot img{ filter:blur(1.8px) saturate(.8); opacity:.5; transition:none }
.magnify .shot:hover img,.magnify .shot:focus-visible img{ filter:blur(1.2px) saturate(.9); opacity:.62 }

.magnify-detail{
  position:absolute; right:4%; top:50%; transform:translateY(-50%);
  width:48%; height:auto; pointer-events:none;
  border:2px solid var(--income); border-radius:12px;
  /* Two shadows: a tight one that reads as a lifted edge, and a wide soft one that reads as
     distance from the surface behind. drop-shadow rather than box-shadow, because the panel's
     corners are rounded and a box shadow would outline a rectangle that is not there. */
  filter: drop-shadow(0 6px 12px rgba(0,0,0,.75)) drop-shadow(0 30px 56px rgba(0,0,0,.95));
}
@media (max-width:700px){
  /* KEPT ON A PHONE, at Steve's ask, 2026-09-12: "It's perfect on Desktop." This used to set
     the panel under the screenshot on its own below 700px, which lost the whole effect. On a
     phone the screenshot is short and the panel is tall, so the panel is drawn larger against
     it and the figure gets more room above and below for the panel to stand out into. */
  /* The panel is about 0.9 of the figure's width tall and the screenshot about 0.53, so the room
     above and below is written against the width, which is what a percentage padding measures. */
  .magnify{ padding:calc(18.5% + 16px) 0 }
  .magnify-detail{ right:3%; width:58%; border-radius:10px }
}
@media (max-width:860px){
  /* Two across, then one. Three phones at 300px each is three unreadable phones. */
  .phones{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s-4) }
}
@media (max-width:560px){
  .phones{ grid-template-columns:1fr }
}
.split.flip .split-copy{order:2}
.split-copy h3{margin-bottom:12px}
.split-copy .eyebrow{margin-bottom:12px}

/* A CAPTION MUST NOT DRAG THE COPY DOWN. Steve, of the paragraph beside the insurance
   picture: "it's more towards the bottom of that image whereas it needs to be
   equidistant between the bottom and top." It was centred on the FIGURE, and a figure
   is the picture PLUS its caption, so the figure's middle sits below the picture's
   middle by half the caption. Taking the figure's own box out of the grid makes the
   picture and the caption two rows of the split, and the copy then centres on the
   picture's row alone. The caption keeps its element and its meaning; only its box
   stops nesting. */
/* .split sets gap:52px, which is a ROW gap as well as a column one, so the caption
   landing in its own row picked up 52px it never used to have. The caption's own
   12px margin is the spacing it wants; the rows get none. */
.split.capped{row-gap:0}
.split.capped > figure{display:contents}
.split.capped .shot{grid-column:2;grid-row:1}
.split.capped figcaption{grid-column:2;grid-row:2}
.split.capped .split-copy{grid-column:1;grid-row:1;align-self:center}
.split.capped.flip .shot,.split.capped.flip figcaption{grid-column:1}
.split.capped.flip .split-copy{grid-column:2}
@media (max-width:900px){
  .split{grid-template-columns:1fr;gap:26px}
  .split.flip .split-copy{order:0}
  /* One column again, so the explicit placement above has to be undone or a
     grid-column:2 would invent a second column off the side of the phone. */
  .split.capped .split-copy,.split.capped .shot,.split.capped figcaption,
  .split.capped.flip .split-copy,.split.capped.flip .shot,.split.capped.flip figcaption
    {grid-column:1;grid-row:auto}
  /* One column, so the rows ARE the layout again and they want the gap back. The
     caption is the exception: it still belongs to the picture above it. */
  .split.capped{row-gap:26px}
  .split.capped figcaption{margin-top:-14px}
  .split + .split{margin-top:88px}
}

/* ------------------------------------------------------------------- cards */
.cards{display:grid;gap:18px}
.cards > *{min-width:0}
.cards.c2{grid-template-columns:repeat(2,minmax(0,1fr))}
.cards.c3{grid-template-columns:repeat(3,minmax(0,1fr))}
.cards.c4{grid-template-columns:repeat(4,minmax(0,1fr))}
@media (max-width:980px){ .cards.c3,.cards.c4{grid-template-columns:repeat(2,1fr)} }
@media (max-width:660px){ .cards.c2,.cards.c3,.cards.c4{grid-template-columns:1fr} }

.card{
  background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  padding:26px 24px;position:relative;overflow:hidden;
}
.card h3{margin-bottom:10px}
.card p{font-size:15.2px;line-height:1.58;color:var(--muted)}
/* Names the one edition a card is true of, 2026-09-17: the trip and receipt card on the front page
   is Contractor Edition only. Set like .tick, which it sits beside in spirit. */
.card .card-ed{
  font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim);margin:0 0 12px;line-height:1.3;
}
.card .tick{
  font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim);display:block;margin-bottom:14px;
}
/* THE EDITION'S OWN LOGO where its name used to be typed, at Steve's word. The three files
   were trimmed to their own edges and scaled to one width, so the GLOBAL OFFICE SYNC wordmark
   is the same size in all three and only the edition mark underneath differs. The box is a
   fixed height, set by the tallest logo, so the headings below still start on one line across
   the row: the Bookkeeper logo carries no edition stamp and is less than half as tall. */
.edition-mark{ height:44px; margin-bottom:var(--s-3); display:flex; align-items:flex-start }
.edition-mark img{ display:block; width:100%; max-width:296px; height:auto }
/* Centered in the front page's cards. On a desktop the card is about as wide as the logo, so
   it only LOOKED centered; on a phone the card is wider and the logo sat against the left. */
#editions .edition-mark{ justify-content:center }
/* The three cards sit side by side on the front page, so the plain wordmark takes the same room
   below it as the two stamped ones and the headings and text under all three start on one line.
   It sat 20px higher until Steve saw it, 2026-09-12. The editions page stacks its sections, so
   there is nothing there to line up with. */
#editions .edition-mark{ margin-bottom:calc(var(--s-3) + 20px) }
/* Solo's "Coming" lives INSIDE the room the other two cards leave under their logos, so its
   heading starts level with theirs instead of a line lower, and it is centered under the logo
   it belongs to. Steve, 2026-09-12. The 36px under a logo is split 9 above the word, its own 17,
   and 10 below it. */
#editions .card.soon .edition-mark{ margin-bottom:9px }
#editions .card.soon .tick{ margin-bottom:10px; text-align:center }
/* Every card's heading takes two lines' room, so the text under all three starts level even at a
   width where one heading happens to fit on one line. Steve: "I'm a stickler for symmetry." */
#editions .card h3{ min-height:calc(2em * 1.25) }
/* On a phone the editions page is one column, and each section's logo is centered on the page
   rather than sitting against the left, at Steve's word, 2026-09-12. Solo's "Coming" under its
   logo comes with it. 900px is where these rows fall to one column. */
@media (max-width:900px){
  .split-copy > .edition-mark{ justify-content:center }
  .split-copy > .edition-mark + .eyebrow{ text-align:center }
}
/* 20px more under the two logos that carry an edition stamp, at Steve's word, off the 8px
   scale on purpose. The stamp fills its box to the bottom edge, where the plain Bookkeeper
   wordmark leaves 25px of its own box empty, so without this the two stamped cards read as
   crowded against their headings. */
.edition-mark.stamped{ margin-bottom:calc(var(--s-3) + 20px) }
/* On the price list the mark IS the plan's heading, and it sits beside the flag in the corner,
   so it is drawn smaller than on the front page. */
.plan .edition-mark{ height:34px; margin-bottom:var(--s-3) }
.plan .edition-mark.stamped{ margin-bottom:calc(var(--s-3) + 14px) }
.plan .edition-mark img{ max-width:230px }
/* Centered on every card. The Coming flag is the only thing left in a corner, and the mark sits
   below it, so the padding the plan heading kept clear for a flag is not needed here. */
.plan .edition-mark{ justify-content:center; padding-right:0 }
/* The comparison table heads its columns with the marks rather than typed names. */
table.grid thead th.grid-mark{ vertical-align:top }
table.grid th.grid-mark img{ display:block; width:100%; max-width:200px; height:auto }

/* The connection's two screens at 85% of the column, at Steve's "make the pics smaller if
   you need to": each carries one paragraph and at full width outweighed it. */
.connect-shot{ margin:0 }
.connect-shot .shot{ width:85% }
/* 40px more above it than the scale gives, at Steve's word, 2026-09-12. */
/* Keeps its top gap and loses the centring. It is a .notice box now, which brings its own frame
   and left-aligned text; text-align:center here was the thing Steve asked to be rid of. */
.connect-close{ margin-top:calc(var(--s-6) + 40px) }
/* Asking a client for documents sits under the connection, 2026-09-17: the answer for a client who
   will never use Sync. Its head starts a new part of the section, so it gets a section's worth of air. */
.client-docs-head{ margin-top:140px }
.h-aside{ color:var(--dim); font-weight:400 }
@media (max-width:900px){ .connect-shot .shot{ width:100% } }

/* The client's document request page, shot at phone width. A phone screen in a full half column
   would stand taller than the whole page, so it is held to about the size a phone is in the hand
   and centered in its column. It is a real screen of the running app, framed like every other
   screenshot and never drawn into a phone. */
.request-phone{ margin:0; display:flex; justify-content:center }
.request-phone .shot{ width:100%; max-width:300px }
.card::before{
  content:"";position:absolute;left:0;top:0;height:2px;width:52px;
  background:var(--accent,var(--line));
}

/* ------------------------------------------------------------- step list */
.steps{counter-reset:step;display:grid;gap:2px;margin:0;padding:0;list-style:none}
.steps li{
  counter-increment:step;position:relative;
  padding:22px 24px 22px 74px;
  background:var(--card);border:1px solid var(--line);
  border-radius:var(--radius);
}
.steps li::before{
  content:counter(step);position:absolute;left:24px;top:21px;
  font-family:var(--mono);font-size:12px;font-weight:500;
  color:var(--income);
  width:28px;height:28px;border-radius:8px;
  border:1px solid var(--line);background:var(--bg-alt);
  display:flex;align-items:center;justify-content:center;
}
.steps li b{display:block;margin-bottom:4px}
.steps li p{font-size:15.2px;color:var(--muted);margin:0}
@media (max-width:660px){ .steps li{padding:20px 18px 20px 62px} .steps li::before{left:18px} }

/* -------------------------------------------------------------- plain list */
ul.plain{list-style:none;margin:0;padding:0}
/* ul.plain zeroes its margin and outranks .next; a list that follows a picture still wants the gap. */
ul.plain.next{margin-top:var(--s-4)}
ul.plain li{
  position:relative;padding:0 0 0 18px;margin:0 0 11px;
  font-size:15.4px;line-height:1.56;color:var(--body);
}
ul.plain li::before{
  content:"";position:absolute;left:0;top:.66em;width:8px;height:2px;
  background:var(--accent,var(--dim));
}
ul.plain li b{color:var(--text)}
ul.plain.two{columns:2;column-gap:40px}
ul.plain.two li{break-inside:avoid}
@media (max-width:760px){ ul.plain.two{columns:1} }

/* ------------------------------------------- headline and shout lines */
/* The second half of the headline carries the whole inversion, so it is the half that
   gets the color. "Everything else files your paper" is the setup; "This one puts it to
   work" is the turn. */
.hl{color:var(--income)}

/* "Ever." SITS ALONE ON ITS OWN LINE AND THAT IS DELIBERATE, from the approved copy: it
   is the only single word line on the page, so the eye stops there whether it meant to
   or not, and it stops on the promise that separates this from every automated
   competitor. Do not merge it into the sentence above it. */
.shout{
  font-size:clamp(20px,2.9vw,28px);line-height:1.2;letter-spacing:-.018em;
  /* 48px above, not 34. Steve: "add another 10px to 20px padding underneath 'It's what
     happens when the paper finally has something to talk to' so the next line doesn't
     look as crowded." That is the gap this margin makes, and 48 is 14 more than it was.
     Written as two scale steps rather than as 48, and the 34 it replaces was off the
     scale entirely. */
  color:var(--text);font-weight:600;margin:calc(var(--s-5) + var(--s-3)) 0 0;
}
.shout.ever{color:var(--income);margin-top:6px}

/* A line pulled out of running copy without breaking the column. */
.pull-inline{
  color:var(--text);font-size:clamp(17px,2.1vw,19.5px);font-weight:500;
  line-height:1.35;letter-spacing:-.012em;
  border-left:2px solid var(--income);padding-left:18px;
  margin:22px 0 22px;
}

/* An aside that deliberately does NOT match the cards above it. The approved ad this
   section comes from carries its own note about why: all four examples share the same
   shape, observation then consequence, four times running, and the reader locks onto that
   rhythm by the third, so the fourth arrives with less force than it deserves. Breaking
   the pattern on the last one is the lever, and this is that. */
/* IT RUNS THE FULL WIDTH OF THE THREE CARDS ABOVE IT, WITH THE SAME INSET ON BOTH
   SIDES. Steve: "it needs to run the full width of the three boxes above it, or rather
   they need the same margin on the right and the left. So if aside has it pushed over a
   little from the left, make sure there is an equal margin on the right."

   Two things were wrong and only one of them was the one that showed. The obvious one:
   a 66ch cap held the line short, so the paragraph broke to three lines and stopped well
   before the right edge of the cards, which made the whole block look like it had slid
   left. The real one: the rule and its padding inset the text 21px from the left with
   nothing answering it on the right, so the inset read as a misalignment rather than as
   a margin. Matching the right to the left makes it a frame, and dropping the cap lets
   the measure use the width the cards already establish. */
.aside{
  /* Room above and below, at his ask. The space BELOW is set on .talk-shot rather than
     here, because adjacent margins collapse to the larger of the two: a margin-bottom on
     this paragraph would have been swallowed by the figure's margin-top and changed
     nothing at all on the screen. */
  margin:calc(var(--s-4) + var(--s-2)) 0 0;
  padding:0 21px;
  border-left:1px solid var(--line);
  font-size:16px;line-height:1.62;color:var(--muted);font-style:normal;
}

/* ------------------------------------------------------------------ quote */
.pull{
  border-left:2px solid var(--income);
  padding:4px 0 4px 22px;margin:0;
  font-size:clamp(19px,2.6vw,25px);line-height:1.36;font-weight:300;
  letter-spacing:-.014em;color:var(--text);
}
.pull cite{
  display:block;font-style:normal;font-family:var(--mono);
  font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim);margin-top:16px;
}

/* ------------------------------------------------------------------ prices */
.plans{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;align-items:stretch}
.plans > *{min-width:0}
@media (max-width:940px){ .plans{grid-template-columns:1fr} }
.plan{
  display:flex;flex-direction:column;
  background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  padding:28px 26px 26px;position:relative;overflow:hidden;
}
.plan::before{content:"";position:absolute;left:0;top:0;height:3px;width:100%;background:var(--accent,var(--line));opacity:.85}
.plan.featured{border-color:var(--line-warm);background:linear-gradient(180deg,var(--card-lift),var(--card))}
.plan .flag{
  position:absolute;right:0;top:0;
  font-family:var(--mono);font-size:10px;letter-spacing:.15em;text-transform:uppercase;
  color:var(--ink-on-accent);background:var(--income);padding:5px 12px;border-bottom-left-radius:10px;font-weight:500;
}
.plan .who{
  font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim);margin:6px 0 12px;
  /* Two lines' worth, always, so the three prices sit on one line across the row
     however long one edition's description happens to be. */
  min-height:2.9em;
}
@media (max-width:940px){ .plan .who{min-height:0} }
/* The corner flag is absolutely positioned, so the title has to be told to stay out
   from under it. */
.plan h3{font-size:22px;margin-bottom:2px;padding-right:96px}
.plan .price{margin:18px 0 4px;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.plan .price .amount{font-size:42px;font-weight:700;color:var(--text);letter-spacing:-.03em;line-height:1}
.plan .price .unit{font-size:14px;color:var(--muted)}
.plan .plus{font-size:14.5px;color:var(--muted);margin:0 0 18px}
.plan .plus b{color:var(--text)}
.plan .alt-price{
  border-top:1px solid var(--line-soft);border-bottom:1px solid var(--line-soft);
  padding:14px 0;margin:0 0 20px;font-size:14.5px;color:var(--muted);
}
.plan .alt-price b{color:var(--text)}
.plan ul.plain{margin-bottom:24px}
.plan ul.plain li{font-size:14.6px;color:var(--muted)}
.plan ul.plain li b{color:var(--body);font-weight:500}
.plan .plan-cta{margin-top:auto}
.plan .plan-cta .btn{width:100%}
/* The line under the button is centered on it, at Steve's word, 2026-09-15. */
.plan .plan-cta .note{text-align:center}
.plan.soon{opacity:.92}
.plan.soon .flag{background:var(--taxdocs);color:var(--ink-on-taxdocs)}
/* THE BUY BUTTONS WEAR THEIR EDITION'S LOGO COLOR. Steve, 2026-09-14: Full Edition "the same
   green as the word Sync in the logo", Contractor "the same Red as Contractors Edition in the
   logo", and Solo "the same gold color as is in the logo". Each color is sampled off the logo file
   itself. Solo's waitlist button is filled like the other two now. Hover lightens, color only. */
#bookkeeper .plan-cta .btn{background:var(--ed-full);border-color:var(--ed-full);color:var(--ink-on-accent)}
#bookkeeper .plan-cta .btn:hover{background:var(--ed-full-hover);border-color:var(--ed-full-hover)}
#contractor .plan-cta .btn{background:var(--ed-contractor);border-color:var(--ed-contractor);color:var(--text)}
#contractor .plan-cta .btn:hover{background:var(--ed-contractor-hover);border-color:var(--ed-contractor-hover)}
#solo .plan-cta .btn{background:var(--ed-solo);border-color:var(--ed-solo);color:var(--ink-on-taxdocs)}
#solo .plan-cta .btn:hover{background:var(--ed-solo-hover);border-color:var(--ed-solo-hover)}
/* And the line across the top of each card, at his word the same day. */
#bookkeeper.plan::before{background:var(--ed-full)}
#contractor.plan::before{background:var(--ed-contractor)}
#solo.plan::before{background:var(--ed-solo)}

/* ------------------------------------------------------------------ notice */
.notice{
  border:1px solid var(--line);border-left:2px solid var(--taxdocs);
  border-radius:var(--radius);background:var(--card-2);
  padding:20px 22px;font-size:15px;color:var(--muted);
}
.notice b{color:var(--text)}

/* -------------------------------------------------------------- comparison */
.table-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;border:1px solid var(--line);border-radius:var(--radius)}
table.grid{border-collapse:collapse;width:100%;min-width:640px;font-size:14.8px}
table.grid th,table.grid td{
  text-align:left;padding:14px 18px;border-bottom:1px solid var(--line-soft);
  color:var(--muted);font-weight:300;vertical-align:top;
}
table.grid thead th{
  color:var(--text);font-weight:600;font-size:14px;
  background:var(--card-2);border-bottom:1px solid var(--line);
  position:sticky;top:0;
}
table.grid tbody tr:last-child td{border-bottom:0}
/* THE ROW LABEL HAS TO STAY PUT WHILE THE TABLE IS SWIPED. On a phone this table is wider
   than the screen and scrolls inside its own frame, which is correct, but with the first
   column scrolling away too a reader ends up looking at a column of "yes" with nothing
   saying yes to what. It is pinned, and it needs its own background or the cells sliding
   under it show through. */
table.grid td:first-child,table.grid th:first-child{
  color:var(--body);
  position:sticky;left:0;z-index:1;
  background:var(--card);
  box-shadow:1px 0 0 var(--line-soft);
}
table.grid thead th:first-child{background:var(--card-2);z-index:2}
.table-scroll{position:relative}
table.grid td b{color:var(--text)}
.yes{color:var(--income);font-family:var(--mono);font-size:13px}
.no{color:var(--faint);font-family:var(--mono);font-size:13px}

/* --------------------------------------------------------------- footnotes -
   A claim with a number on it gets a footnote rather than a hedge inside the sentence.
   "About ten to twenty seconds depending on your document and connection" is a sentence
   nobody finishes; a small 1 costs the reader nothing and the qualification is there for
   whoever wants it. The number itself was measured, not estimated. */
.fn-mark{
  font-size:.62em;vertical-align:super;line-height:0;margin-left:1px;
  color:var(--income);text-decoration:none;font-weight:600;
}
.fn-mark:hover{text-decoration:underline}
ol.footnotes{
  list-style:none;counter-reset:fn;margin:0 0 26px;padding:0;
  border-top:1px solid var(--line-soft);padding-top:22px;
}
ol.footnotes li{
  counter-increment:fn;position:relative;padding-left:22px;
  font-size:13.4px;line-height:1.6;color:var(--dim);max-width:78ch;
}
ol.footnotes li::before{
  content:counter(fn);position:absolute;left:0;top:0;
  font-size:11px;color:var(--income);font-weight:600;
}
ol.footnotes li + li{margin-top:10px}
ol.footnotes li:target{color:var(--muted)}
.fn-back{margin-left:8px;color:var(--muted);font-size:12.5px}

/* ------------------------------------------------------------------ footer */
.site-foot{
  border-top:1px solid var(--line-soft);padding:52px 0 64px;
  background:var(--bg-alt);font-size:14.5px;color:var(--dim);
}
/* The link lists sit on the left under the logo and its line, at Steve's word, 2026-09-15, instead
   of in two columns off to the right of it: the left edge is where every section above starts. */
.site-foot .foot-grid{
  display:grid;grid-template-columns:repeat(2,max-content);justify-content:start;
  column-gap:88px;row-gap:28px;margin-bottom:40px;
}
.site-foot .foot-brand{grid-column:1 / -1}
@media (max-width:760px){ .site-foot .foot-grid{column-gap:48px} }
.site-foot img{height:22px;width:auto;margin-bottom:16px}
/* h2 SINCE 2026-09-17, h4 KEPT SO NOTHING BREAKS. A footer column heading was an h4 sitting
   under the page's h2 sections, which skips level 3 on every page of the site and has done
   since the footer was written. The tag changed and not one pixel did. */
.site-foot h4,.site-foot h2{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--dim);margin:0 0 14px;font-weight:400;font-variation-settings:normal}
.site-foot ul{list-style:none;margin:0;padding:0}
.site-foot li{margin-bottom:9px}
.site-foot a{color:var(--muted);text-decoration:none}
.site-foot a:hover{color:var(--text)}
.site-foot .tagline{color:var(--muted);max-width:38ch;margin:0 0 10px}
.site-foot .legal{border-top:1px solid var(--line-soft);padding-top:22px;font-size:13.2px;line-height:1.7}

/* -------------------------------------------------------- reduced motion */
html{scroll-behavior:smooth}
/* Every transition on this site runs on --t and every one of them is a color change, so
   setting it to zero stops all of them. No blanket selector, no !important, nothing for a
   later rule to beat. scroll-behavior is turned off on its own line below, which wins
   because it comes later at equal specificity. */
@media (prefers-reduced-motion:reduce){
  :root{ --t:0s }
  html{ scroll-behavior:auto }
}
:target{scroll-margin-top:86px}

/* Focus that is visible without being ugly. */
a:focus-visible,.btn:focus-visible{outline:2px solid var(--income);outline-offset:3px;border-radius:8px}


/* ============================================================================
   THE PICTURE, OPENED. See site/js/zoom.js for the behavior and why each part
   of it is there.
   ========================================================================== */
/* THE ONE LINE BELOW IS WHY THE WHOLE SITE STOPPED RESPONDING, and it is worth a paragraph
   because it is invisible in every way something can be invisible.

   `.zoom-overlay` sets `display:flex`. The browser's own stylesheet says `[hidden]{display:
   none}`. A class selector has higher specificity than an attribute selector in the user
   agent sheet, so `display:flex` WON: setting `hidden = true` in script made no difference
   to the layout at all. From the first time anybody opened a picture, a fixed, full screen,
   z-index 200, fully transparent sheet sat over the page for the rest of the visit and
   swallowed every click on it. Steve: "once a modal has been opened then closed, NOTHING
   works. Links, nothing."

   It also explains why opening seemed to work "once in a blue moon": the first click landed,
   and every click after it hit the invisible sheet instead of the picture.

   THE BROWSER TEST PASSED THROUGH ALL OF THIS, three times, because it called `.click()` on
   the element from script. A synthetic click is dispatched straight at its target and never
   hit-tests, so it cannot see anything covering the page. It clicks at a POINT now, the way
   a person does, and it presses a link afterwards to prove the page still works. */
.zoom-overlay:not([hidden]){
  position:fixed;inset:0;z-index:200;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:14px;padding:56px 20px 24px;
  background:rgba(4,4,7,.975);
  opacity:0;transition:opacity var(--t) ease;
}
/* THE CASCADE IS FIXED RATHER THAN SHOUTED OVER, which is what section 5 asks for and
   what !important was doing here before. The display rule above is scoped to
   :not([hidden]), so when the attribute is present nothing sets display at all and the
   browser's own [hidden]{display:none} applies with nothing to beat.
   The fault this guards against is worth restating: `.zoom-overlay{display:flex}` used
   to win over the browser's rule, which left an invisible full screen sheet over the
   page after the first close, swallowing every click on the site. */
/* And a second, independent guard, because the fault was that ONE mechanism silently failed:
   even mid-fade, before `hidden` goes back on, nothing that is not open may take a click. */
.zoom-overlay:not(.is-open){pointer-events:none}
.zoom-overlay.is-open{opacity:1;pointer-events:auto}
/* IT FITS. `max-width:none` was on the image, which meant a 2400px wide screenshot stayed
   2400px wide and the frame had to be scrolled to see any of it. The whole picture is on
   screen at once now, and the frame is sized BY the image rather than the other way round,
   so there is no letterbox gap between the border and the picture.

   `min-height:0` on the frame is what lets a flex item shrink below its content, without
   which the image would push the frame past the bottom of the window and be cut off there
   instead. Same family of fault as the grid columns further up this file. */
.zoom-frame{
  flex:0 1 auto;min-width:0;min-height:0;
  /* THE CEILING IS ON THE FRAME, NOT ONLY ON THE IMAGE. Without it the frame simply grew to
     whatever the picture's natural size was when zoomed in, so scrollWidth equalled
     clientWidth, there was nothing to pan, and "zoomed in" did nothing visible at all. The
     frame is pinned to the window; the picture is what gets bigger inside it. */
  max-width:100%;max-height:calc(100vh - 150px);
  /* CENTRED WITH AUTO MARGINS ON THE PICTURE, NOT WITH justify-content. A flex container
     that centres its items makes the overflow on the START side unreachable: scrolled all
     the way left, the first 500px of a zoomed screenshot simply could not be got at, and
     scrollWidth came back as 1899 on a 2400px image. Auto margins centre it and leave both
     ends reachable. */
  display:flex;overflow:hidden;
  border:1px solid var(--line);border-radius:12px;background:var(--card);
  box-shadow:0 40px 100px -40px var(--shadow);
}
.zoom-img{
  display:block;margin:auto;
  max-width:100%;max-height:100%;
  width:auto;height:auto;
  object-fit:contain;
  cursor:zoom-in;
}

/* CLICKING THE PICTURE GOES CLOSER, because "it fits" and "I can read the small print" are
   two different wants and a marketing screenshot has to serve both. Zoomed in, the frame is
   the thing that scrolls and the picture is at its own size. */
.zoom-frame.is-zoomed{overflow:auto;-webkit-overflow-scrolling:touch}
.zoom-frame.is-zoomed .zoom-img{
  max-width:none;max-height:none;width:auto;height:auto;cursor:zoom-out;
  /* `flex:0 0 auto` IS THE WHOLE ZOOM. The picture is a flex item, and a flex item shrinks
     to its container by default however large its own content is: lifting the max-width off
     it changed nothing at all, because flex-shrink went on squashing a 2400px image back to
     the 1339px frame. Measured, not guessed: shown stayed [1339, 848] after zooming and
     there was nothing to pan. */
  flex:0 0 auto;
}
.zoom-frame.at-size .zoom-img{cursor:default}
.zoom-frame::-webkit-scrollbar{width:8px;height:8px}
.zoom-frame::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:4px}

@media (max-width:760px){
  .zoom-overlay{padding:52px 10px 14px}
}
.zoom-close{
  position:absolute;right:16px;top:14px;
  font-family:var(--sans);font-size:14px;font-weight:600;
  color:var(--text);background:rgba(255,255,255,.08);
  border:1px solid var(--line);border-radius:9px;
  padding:8px 16px;cursor:pointer;
}
.zoom-close:hover{background:rgba(255,255,255,.14)}
.zoom-close:focus-visible{outline:2px solid var(--income);outline-offset:3px}
.zoom-caption{
  margin:0;max-width:70ch;text-align:center;
  font-size:13.5px;color:var(--muted);
}
@media (prefers-reduced-motion:reduce){ .zoom-overlay{transition:none} }

/* ---------------------------------------------------------- run-on list ---
   Six short observations read as a human wrote them when they run as
   paragraphs with a lead phrase. Six identical cards in a grid, each with a
   three word title and two lines under it, is the shape of a generated page.
   Same content, and the difference is entirely in the reading. */
.runon{columns:2;column-gap:44px;margin:0}
.runon p{
  break-inside:avoid;margin:0 0 22px;font-size:15.6px;line-height:1.56;
  color:var(--muted);
}
.runon p .lead{color:var(--text);font-weight:600}
.runon p b{color:var(--body);font-weight:500}
@media (max-width:820px){ .runon{columns:1} .runon p{margin-bottom:18px} }

/* ============================================================================
   THE CLASSES THAT REPLACED 95 INLINE STYLE ATTRIBUTES.

   DESIGN_RULES.md section 5 bans inline styles, and it also bans utility-class
   soup, which rules out the obvious escape of `.mt-16 .mb-24 .text-center`. So
   these are named for WHAT THEY MEAN rather than for what they measure, which is
   also what section 2 asks of spacing: "Spacing should signal relationship,
   tighter where things belong together, looser at real breaks."

   Every value comes off section 0's 8px scale. Where one is off the scale it
   carries a comment saying why, which is the rule the scale came with.
   ========================================================================== */

/* ---- how far a thing sits from the thing above it ---- */
.close  { margin-top:var(--s-3) }   /* still the same thought */
.next   { margin-top:var(--s-4) }   /* the next thing in it */
.apart  { margin-top:var(--s-5) }   /* a real break inside a section */
.break  { margin-top:var(--s-6) }   /* a break between ideas */
.lead-in{ margin-bottom:var(--s-2) } /* a heading sitting directly on its list */
.trail  { margin-bottom:var(--s-4) }

/* ---- line length, which section 2 wants to VARY by context rather than be one
        max-width everywhere ---- */
.measure-s{ max-width:36ch }
.measure-m{ max-width:48ch }
.measure-l{ max-width:58ch }

/* ---- which ledger a thing is about ----
   Section 0's signature move. The accent is never decoration: an element carries
   one of these only when the reader is looking at that ledger, and the attribute
   says which so the markup means something a class name like `.blue` would not. */
[data-ledger="receipts"]  { --accent:var(--receipts) }
[data-ledger="income"]    { --accent:var(--income) }
[data-ledger="utilities"] { --accent:var(--utilities) }
[data-ledger="mileage"]   { --accent:var(--mileage) }
[data-ledger="taxdocs"]   { --accent:var(--taxdocs) }
[data-ledger="compliance"]{ --accent:var(--compliance) }
[data-ledger="quotes"]    { --accent:var(--quotes) }
[data-ledger="donations"] { --accent:var(--donations) }
[data-ledger="alert"]     { --accent:var(--red) }
[data-ledger="none"]      { --accent:var(--dim) }
.eyebrow[data-ledger]{ color:var(--accent) }

/* ---- small print under a picture or a figure ----
   Left aligned, because section 2 says paragraphs are left aligned and a centred
   caption under a left aligned page is the template habit it is warning about. */
.note{
  margin-top:var(--s-3);
  font-size:13.5px;color:var(--dim);
}
.subtle{ color:var(--muted) }

/* ---- the hero's two heights ---- */
.hero.tall { padding:var(--s-7) 0 var(--s-7) }
.hero.short{ padding-bottom:var(--s-6) }
/* The price list: the cards follow the headline directly, with no rule and no second band's
   worth of padding between them, so the headline reads as the cards' own heading. */
.hero.short.into-plans{ padding-bottom:var(--s-5); border-bottom:0 }
.band.from-hero{ padding-top:0; border-top:0 }

/* ---- one comparison column that must not collapse onto two lines ---- */
.col-name{ min-width:230px }

/* One band sits directly under the one above it with no rule between them, because the
   two are the same thought continued and a line there would say otherwise. */
.flush-top{ border-top:0; padding-top:0 }

/* ============================================================================
   THE HERO'S PHONE.

   Steve, 2026-09-10: *"I want one of the phone pics up next to the first hero
   text, the phone to the right. Add some perspective, or skew, or whatever to
   the phone and make it look cool."*

   IT IS A PHOTOGRAPH OF A PHONE, NOT A PHONE DRAWN IN CSS. The earlier attempt
   drew the device out of a rounded rectangle and a gradient and he threw the whole
   thing out for it: "the fake phones look terrible." He cut these out himself, so
   the file carries the real handset with real light on its edges and a real alpha
   channel, and this stylesheet's only job is to place it and tilt it.

   THE SHADOW IS A FILTER, NOT A BOX-SHADOW, and that is the one thing here that is
   not interchangeable. `box-shadow` draws the shadow of the element's RECTANGLE, so
   against this ground it would put a hard rectangular smudge behind a rounded
   phone and give the cutout away instantly. `drop-shadow` follows the alpha
   channel, so the shadow is the shape of the phone. Two of them stacked, a tight
   one for contact and a wide one for distance, which is how a real object sits.

   THE TILT IS A STATIC TRANSFORM AND NOT AN ANIMATION. DESIGN_RULES.md section 2
   bans motion, and this is not motion: it is the angle the thing is seen from, the
   same as if it had been photographed that way. Nothing moves on scroll or hover.
   ========================================================================== */
/* THE COLUMN WIDTHS ARE MEASURED, NOT CHOSEN. Steve: "make the phone smaller so that
   the hero text wraps like it did before adding the phone. The phone should be roughly
   the same height as the two smaller paragraphs."

   Measured in the browser at 1440 rather than estimated: "Everything else files your
   paper." needs 820px to sit on one line at 62px Bricolage.

   THE BUDGET IS 1072, NOT 1120, and getting that wrong once already cost a wrapped
   headline: .wrap is 1120 wide but carries 24px of padding on each side, so the grid only
   ever has 1072 to divide. 1072 less 224 of phone less 24 of gap leaves 824, which clears
   820 by four pixels. That is deliberately thin and it is why the phone column is capped
   rather than flexible: if the headline ever grows a word, this is the number to cut.

   224 sits between the 380 that was too big and the 152 that was too small, and it is
   also the widest this phone can be without breaking the headline he asked to keep.

   The paragraphs keep their own measure (60ch) whatever the column does, so widening the
   column does not change their height and the 385 stays true. */
.hero-split{
  display:grid;
  grid-template-columns:minmax(0,1fr) 224px;
  gap:var(--s-4);
  /* BOTTOM ALIGNED, NOT CENTRED, and that is the whole trick. Steve asked for the phone
     to "stretch down to be beside" the fineprint line at the foot of the copy. Centred,
     that is arithmetic: the phone has to be exactly as tall as the copy column or it
     lands somewhere else, and it stops being true the moment a word is added to a
     paragraph. Aligned to the end, the phone's foot sits on the fineprint's foot whatever
     either one does, and its height only decides how far UP it reaches. */
  align-items:end;
}
.hero-split > *{min-width:0}
/* THE PHONE COMES FIRST IN THE MARKUP, and the two columns are named here so the copy still sits
   on the left. It has to precede the copy for the phone layout below, where the words wrap around
   it: a float only wraps the text that follows it. */
/* BOTH IN ROW 1, NAMED. Without the row, the copy could not be placed back in column 1 of a row
   the phone had already started, so it fell to a row of its own and the desktop hero came apart. */
.hero-copy{grid-column:1;grid-row:1}
.hero-phone{grid-column:2;grid-row:1}

/* LIFTED SO THE LOW CORNER LANDS ON THE FINEPRINT. Steve: "move it up so that the
   bottom right of the phone is level with the bottom of the text 'You buy it once and
   own it...'. Maybe 15px." Measured rather than guessed: the tilt puts the phone's
   bottom right 27px below that line, because rotateZ turns the right edge downward and
   the bounding box grows with it. 27 is what makes them level. */
.hero-phone{ perspective:1200px; margin-bottom:27px }
.hero-phone img{
  display:block;width:100%;height:auto;
  transform:rotateY(-17deg) rotateZ(2.5deg) rotateX(3deg) scale(1.06);
  transform-origin:55% 50%;
  /* The shape of the phone, not the shape of its box. See the note above. */
  filter:
    drop-shadow(0 10px 18px rgba(0,0,0,.55))
    drop-shadow(0 44px 70px rgba(0,0,0,.75));
}

/* UNDER 980px THE TILT COMES OFF BEFORE THE LAYOUT DOES. A phone rotated 17 degrees
   in a column that is now narrow reads as a mistake rather than as a point of view,
   and it eats horizontal room it no longer has. */
/* BELOW ABOUT 1180 THE HEADLINE CAN NO LONGER HOLD ITS LINE, because the wrap is
   shrinking with the viewport while the type is still pinned at its 62px ceiling. Rather
   than let it wrap into a shape nobody chose, the ceiling comes down here and the line
   survives another 200px of narrowing. */
@media (max-width:1180px){
  h1{font-size:clamp(34px,5.4vw,56px)}
  .hero-split{grid-template-columns:minmax(0,1fr) 208px}
  .hero-phone img{transform:rotateY(-14deg) rotateZ(2deg) scale(1.03)}
}
/* ON A PHONE IT KEEPS THE TILT, at Steve's word 2026-09-15: "on mobile it doesn't have that cool
   perspective pic of the phone next to the hero text... just a bummer." It lost the tilt here
   because a 17 degree rotation inside a narrow column read as a mistake. Centered in its own row
   and held to 320px it reads as deliberate again, so the angle comes back, gentler, with the
   shadow that makes it sit in the page rather than on it. The tilt swings the picture wider than
   its box, which is what the padding on each side is for. */
/* THE HERO ON A PHONE. Steve, 2026-09-15: "The first paragraph has to be higher. Drop the phone
   another 30px, and have the first paragraph start level with the top of the phone... The second
   paragraph has NO space after the first. That's terrible... the phone is cutting off some of the
   text and the shadow is impeding into the text as well. That second paragraph MUST be a good
   25-30px below the phone, at least!"

   Under 600px the headline keeps the full width, the first paragraph and the picture share the next
   row with their TOPS level, both 30px below the headline, and everything after clears the taller
   of the two by 30px. That needs the copy's children to be grid items, which is what
   display:contents does: the .hero-copy box stops generating anything and its children take its
   place. They are placed by hand because the picture comes first in the markup, which it has to,
   for the layout below.

   A float was tried first and thrown out. It wraps text, but its foot lands wherever its height
   puts it, and no width made that agree with the paragraph's last line. */
@media (max-width:520px){
  .hero-split{
    display:grid;grid-template-columns:minmax(0,1fr) 40%;
    column-gap:24px;row-gap:0;align-items:start;
  }
  .hero-copy{display:contents}
  .hero-copy .eyebrow{grid-column:1 / -1;grid-row:1;white-space:nowrap}
  .hero-copy h1{grid-column:1 / -1;grid-row:2;font-size:30px}
  .hero-copy .lede:not(.turn){grid-column:1;grid-row:3;margin:30px 0 0}
  .hero-copy .lede.turn{grid-column:1 / -1;grid-row:4;margin-top:30px}
  .hero-copy .btn-row{grid-column:1 / -1;grid-row:5}
  .hero-copy .fineprint{grid-column:1 / -1;grid-row:6}
  .hero-phone{
    grid-column:2;grid-row:3;
    /* The 16px under it is for the shadow, which a bounding box does not include: without it the
       next paragraph measured 30px clear and looked 10px clear. */
    width:auto;max-width:none;margin:30px -6px 16px 0;padding:0;perspective:900px;
  }
}

/* A TABLET UPRIGHT, AND A PHONE TURNED SIDEWAYS, get the two plain columns instead: every word in
   the left one, the picture in the right one, both starting at the top. Splitting the copy into
   rows at these widths leaves a hole beside the second paragraph, because the first is only three
   or four lines across a wide column. Landscape used to get the DESKTOP hero, where the picture is
   sized for a tall window: it hung off the foot of the screen with a hole above it. */
@media (min-width:521px) and (max-width:900px), (max-width:1024px) and (max-height:600px){
  .hero-split{
    display:grid;grid-template-columns:minmax(0,1fr) 300px;
    column-gap:28px;align-items:start;
  }
  .hero-copy{display:block}
  .hero-copy h1{font-size:30px}
  .hero-phone{grid-column:2;grid-row:1;width:auto;max-width:none;margin:8px -6px 0 0;padding:0;perspective:900px}
}
/* Sideways there is less height than a phone picture wants, so the column is narrower again. */
@media (max-width:1024px) and (max-height:600px){
  /* 189px is a tenth off the 210 it was, at Steve's ask, so the picture stands about as tall as
     the two paragraphs beside it. The drop goes up with it, from 110 to 130, which keeps the foot
     on the Pricing button and now starts the top level with the first paragraph. */
  .hero-split{grid-template-columns:minmax(0,1fr) 189px;column-gap:26px}
  .hero-copy h1{font-size:27px}
  .hero-phone{margin-top:130px}
}

/* The tilt is the part he liked, and it stays at every size. The shadow is tighter here than on a
   desktop: a 52px blur beside a 190px column of text puts grey behind the words. */
@media (max-width:1024px){
  .hero-copy h1 .hl{display:block;margin-top:20px}   /* 20px between the white line and the green */
  .hero-phone img{
    transform:rotateY(-13deg) rotateZ(2deg) rotateX(2deg) scale(1.02);
    transform-origin:55% 50%;
    filter:
      drop-shadow(0 4px 7px rgba(0,0,0,.48))
      drop-shadow(0 12px 18px rgba(0,0,0,.62));
  }
}

/* ---------------------------------------------- the closing passage of #talk */
/* LEFT ALIGNED, like the rest of the page. It was centered under the picture above it at
   Steve's ask on 2026-09-10; on 2026-09-12 he took it back: "I don't like it. Please left
   align it so it matches the rest of the page content." Nothing here now, on purpose. */

/* ------------------------------------------------------- the three engines */
/* The drop zone, the reading, and the review, in one row directly under the four
   numbered steps they illustrate. Each of the last two used to have a paragraph beside
   it repeating what step 2 and step 3 already said; Steve: "that's redundant
   information, not needed."

   All three pictures are 480 by 720, so at one shared column width the frames line up
   to the pixel with no aspect juggling. Under 900px they go two up and then one up,
   because three 340px screens on a phone is three postage stamps. */
.engines{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--s-4);
  margin-top:var(--s-5);      /* room to breathe under the steps. It was 16 and crowded */
}
.engines > *{min-width:0}
.engines figcaption{text-align:center}
@media (max-width:900px){
  .engines{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* width:100% IS LOAD BEARING. Centered with auto margins, the last frame sized itself to
     its picture, and a lazy picture has no size until it loads: on a phone the frame grew
     91px when it arrived and pushed the whole page down under the reader. Chrome hides that
     by holding the scroll still; Safari does not, so a jump from the contents to the next
     section landed 91px short. Measured in WebKit, 2026-09-12. */
  .engines > *:last-child{grid-column:1 / -1;width:100%;max-width:calc(50% - var(--s-2));margin:0 auto}
}
@media (max-width:620px){
  .engines{grid-template-columns:1fr;max-width:340px;margin-left:auto;margin-right:auto}
  .engines > *:last-child{max-width:none}
}

/* The filed record under the three engine screens. It was 2px, which is what he asked
   for when the row had no captions under it; with the captions in, 2px read as crowded
   and he said so: "let it breathe dude." 40px now, which is well past the 20 he set as
   the floor, and the four pictures still read as one sequence because the captions tie
   the row together and this one carries its own. */
.filed{margin-top:var(--s-5)}
.filed figcaption{text-align:center}

/* ------------------------------------------------------------- the menu bar */
/* The strip of eight buttons, under the paragraph listing what they read. */
/* Off the scale on both margins, at his ask: another 20 above and below what was here.
   36 and 52. Section 0 allows a break from the scale with a reason and that is the
   reason. */
.menubar{
  margin-top:61px;
  margin-bottom:52px;
  border:1px solid var(--line);
  border-radius:var(--radius);
  overflow:hidden;
  box-shadow:
    0 40px 90px -40px var(--shadow),
    0 0 0 1px rgba(255,255,255,.025) inset,
    0 1px 0 rgba(255,255,255,.05) inset;
}
.menubar img{width:100%;display:block}

/* THE KEYS over the picture. One real button over each of the eight, placed by measurement
   rather than by an even split: the icons in the picture are not evenly spaced. Each key runs
   from halfway to its left neighbor to halfway to its right one, the outer two as wide again
   on their open side, all in percentages of the picture so they stay put at any width. The one
   showing wears its ledger's rule along the top, the site's signature move and the app's own
   mark above a ledger button, and here it names the ledger it sits over. */
.menubar{position:relative}
.menubar-keys{position:absolute;inset:0}
.menubar-keys button{
  position:absolute;top:0;bottom:0;
  margin:0;padding:0;border:0;border-radius:0;background:transparent;cursor:pointer;
  transition:background-color var(--t) ease;
}
.menubar-keys button::before{
  content:"";position:absolute;top:0;left:50%;width:44%;height:2px;margin-left:-22%;
  background:var(--accent);opacity:0;transition:opacity var(--t) ease;
}
.menubar-keys button:hover{background-color:rgba(255,255,255,.035)}
.menubar-keys button:hover::before{opacity:.45}
.menubar-keys button[aria-pressed="true"]{background-color:rgba(255,255,255,.05)}
.menubar-keys button[aria-pressed="true"]::before{opacity:1}
.menubar-keys button:focus-visible{outline:2px solid var(--income);outline-offset:-3px}
.menubar-keys button:nth-child(1){left:5.32%;width:10.75%}
.menubar-keys button:nth-child(2){left:16.07%;width:10.94%}
.menubar-keys button:nth-child(3){left:27.01%;width:10.97%}
.menubar-keys button:nth-child(4){left:37.98%;width:10.70%}
.menubar-keys button:nth-child(5){left:48.68%;width:10.91%}
.menubar-keys button:nth-child(6){left:59.59%;width:11.77%}
.menubar-keys button:nth-child(7){left:71.36%;width:11.92%}
.menubar-keys button:nth-child(8){left:83.28%;width:11.56%}
/* The picture above changes with a short dip rather than a hard cut. */
#engine-view{transition:opacity var(--t) ease}
#engine-view.swapping{opacity:.25}

/* 35 more than the 24 .next gives, at his ask. */
.notice.coa-note{margin-top:100px}

/* ------------------------------------------------------------- two looks ---- */
/* Luminous Dark or Tailored Light. The switch sits above one frame holding the same window in
   both looks, the light picture laid exactly over the dark one, and the three screens under it are
   pairs the same way; js/looks.js flips data-look on the section and every light one fades in. Each side of the switch, when pressed, wears its own look:
   dark glass with the accent for Luminous Dark, the app's own paper and ink for Tailored Light. */
.looks{max-width:900px;margin:0 auto}
.look-switch{
  display:flex;width:max-content;max-width:100%;margin:0 auto var(--s-4);padding:4px;gap:4px;
  border:1px solid var(--line);border-radius:999px;background:var(--card);
}
.look-switch button{
  font:inherit;font-size:14px;font-weight:500;line-height:1;cursor:pointer;
  padding:11px 20px;border:0;border-radius:999px;background:transparent;color:var(--muted);
  transition:background-color var(--t) ease, color var(--t) ease;
}
.look-switch button:hover{color:var(--text)}
.look-switch button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.look-switch button[data-look="dark"][aria-pressed="true"]{
  background:var(--card-2);color:var(--text);box-shadow:inset 0 0 0 1px var(--line-mid);
}
.look-switch button[data-look="light"][aria-pressed="true"]{background:var(--paper);color:var(--paper-ink)}
.look-frame img{display:block}
.look-frame .look-light{
  position:absolute;inset:0;height:100%;opacity:0;transition:opacity var(--t) ease;
}
#looks[data-look="light"] .look-frame .look-light{opacity:1}

.look-strip{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4);
  margin-top:var(--s-6);
}
.look-strip .shot img{display:block;aspect-ratio:16/9;object-fit:cover;object-position:top center}
@media (max-width:760px){
  .look-strip{grid-template-columns:1fr;gap:var(--s-5);margin-top:var(--s-5)}
  .look-switch button{padding:10px 16px;font-size:13.5px}
}


/* ============================================================================
   THE RESTRUCTURE, 2026-09-17. The front page was 46 printed pages and every
   visitor had to walk all of it. Steve had it split into layers: the opening
   frame stays exactly as it was, a short front page under it, an audience page
   each, six pages for the parts of the product, and the whole of the old page
   preserved as tour.html.

   NOTHING ABOVE THIS LINE WAS CHANGED, on purpose. The opening frame had to come
   through the split pixel for pixel, and the surest way to promise that is to
   add rules rather than touch any. tools/check_site_first_frame.mjs proves it,
   element by element, at thirty widths in two browsers.
   ========================================================================== */

/* A heading that is in the document and not on the screen. The tour page begins
   at its table of contents with no hero over it, at Steve's word, so its h1 has
   nowhere to be drawn; it still has to exist for the outline and for a screen
   reader. Not display:none, which would take it out of the accessibility tree
   as well, and not a negative text-indent, which drags a focus ring off screen. */
.offscreen{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;
}

/* ---------------------------------------------------------------- the footer */
/* Three columns of links instead of two, because the deeper pages are findable
   from here and from the page bodies and NOT from the top navigation, which
   Steve kept as it was. At 760px it folds to two and at 480 to one, because
   "Information Center" and "If you keep books for others" are long labels and
   three of them will not sit side by side on a phone. */
.site-foot .foot-grid{grid-template-columns:repeat(3,max-content);column-gap:72px}
@media (max-width:980px){ .site-foot .foot-grid{grid-template-columns:repeat(2,max-content);column-gap:56px} }
/* ONE FLEXIBLE COLUMN AT THE BOTTOM END, NOT ONE max-content COLUMN, and that distinction
   IS the fix. A max-content track is as wide as its widest item will go without wrapping,
   and the item spanning every track here is the tagline, whose own max-width is 38ch: about
   300px. So the footer grid measured 340px inside a 320px phone. html,body{overflow-x:hidden}
   then CLIPPED the excess rather than scrolling it, which is why nothing ever looked wrong
   and the end of the tagline was simply not there.
   THIS WAS ALREADY TRUE BEFORE THE RESTRUCTURE, at 320px, on all three pages. It went
   unnoticed because check_site_renders.mjs measured one phone width, 390, and at 390 the two
   short columns fitted. The third column and the longer labels brought the same fault up to
   390, which is how it was found. 320 and 375 are in that harness now. */
@media (max-width:560px){ .site-foot .foot-grid{grid-template-columns:minmax(0,1fr);row-gap:26px} }

/* ------------------------------------------------------- the three paths */
/* The front page's three audience cards carry the same edition logos as the
   editions section further down the tour, so they need the same two fixes that
   section already has: the mark centered in a card that is wider than it, and
   the same room under every mark so the headings across the row start level.
   Those rules are scoped to #editions and this section is #choose, so they are
   restated rather than having the existing selector widened; #editions is on
   tour.html now and the two pages are free to drift. */
#choose .edition-mark{ justify-content:center; margin-bottom:calc(var(--s-3) + 20px) }
#choose .card.soon .edition-mark{ margin-bottom:9px }
#choose .card.soon .tick{ margin-bottom:10px; text-align:center }
/* Every card's heading takes two lines' room so the copy under all three starts
   level even where one heading happens to fit on one line. Three lines here
   rather than the two #editions needs: "I run a contracting or field service
   business" does not fit in two at every width. */
#choose .card h3{ min-height:calc(3em * 1.25) }
@media (max-width:660px){ #choose .card h3{ min-height:0 } }

/* ----------------------------------------------------- the way out of a section */
/* The link to the page that carries the whole answer, out of a card (.card-go) or
   out of the foot of a section (.go-row). Set as links rather than as buttons, and
   that was a correction, not a preference.
   THEY WERE GHOST BUTTONS IN A .btn-row FIRST, AND THAT BROKE THE PHONE. A .btn-row
   holding exactly two buttons is set flex-wrap:nowrap under 560px, at Steve's word
   from 2026-09-12, so that a pair of short labels stays on one line. These labels are
   not short: "See how documents get in" beside "See the whole handoff to accounting"
   measured 375px inside a 342px column, and html,body{overflow-x:hidden} clipped the
   overhang instead of scrolling it, so the second label simply had its end cut off.
   Steve asked for both things here: labels that say where they go, and no horizontal
   overflow on a phone. A button row can only have one of the two. A row of links can
   have both, because it is allowed to wrap.
   The arrow is drawn in --accent, which section 0 reserves for actions and links, and
   inside a card --accent is that card's own ledger colour. */
/* 9px of padding above and below the words, which is the whole point of it: it takes
   the tap target from about 19px tall to about 37px. Steve, that same morning, about a
   14px line of type that was the only way back to the practice dashboard on a phone:
   "hard to tap without giving it 2 or 3 tries." These are secondary links and they are
   wide, so the height was the only thing wrong with them. The margins are pulled in by
   the same 9px at each end so the space around the row looks exactly as it did. */
.card-go{ margin:calc(var(--s-4) - 9px) 0 -9px }
/* Centered inside the three edition cards on the front page, at Steve's ask 2026-09-17.
   The link is an inline-flex box, so it is the paragraph around it that does the centering.
   Only in #choose: everywhere else a .card-go sits under left-aligned copy. */
#choose .card-go{ text-align:center }
.go-row{
  display:flex;flex-wrap:wrap;gap:0 36px;
  margin:calc(var(--s-5) - 9px) 0 -9px;padding:0;list-style:none;
}
.go-row li{ margin:0 }
.card-go a,.go-row a{
  display:inline-flex;align-items:baseline;gap:9px;padding:9px 0;
  color:var(--accent);font-size:14.8px;font-weight:500;text-decoration:none;
  transition:color var(--t) ease;
}
.card-go a::after,.go-row a::after{ content:"\2192"; font-weight:400 }
.card-go a:hover,.go-row a:hover{ color:var(--text) }

/* The product overview's six cards are h2 (they are the top level of that section,
   under the page's h1, with no h2 above them) and are drawn at the size a card
   heading is drawn at, which is what `.card h3` gave them before the tag changed
   for the sake of the heading order. */
#parts .card h2{
  font-size:clamp(18px,2.2vw,21px);font-variation-settings:'opsz' 40;
  letter-spacing:-.012em;line-height:1.25;font-weight:600;margin-bottom:10px;
}

/* --------------------------------------------------- the footer, as a tap target */
/* A footer link was 17px tall with a 9px gap under it, which was survivable while the
   footer held four links and is not now that it holds seventeen: a thumb that misses
   no longer lands on nothing, it lands on the wrong page. The padding makes each one
   about 33px and the margin it replaces kept the column the same height it was.
   Scoped to the three link lists: the "Back" link at the end of a footnote is part of
   a sentence and is left alone. */
.site-foot ul a{ display:inline-block; padding:8px 0 }
.site-foot ul li{ margin-bottom:0 }

/* ------------------------------------------- the closing section's buttons */
/* 60px between the last line and the row of buttons, at Steve's ask, 2026-09-17.
   It is set on the paragraph rather than on the row because .btn-row is used all
   over the site and this gap belongs to this one place. */
#next .lede.close + .btn-row{ margin-top:60px }

/* -------------------------------- a row of links under a row of columns */
/* THE SECOND LINK LINES UP WITH THE SECOND COLUMN. Steve, 2026-09-17: "Every time
   something like this - two columns with two green links underneath - happens, line
   up that second link with the right column. Right now this looks a bit sloppy."
   It was a flex row with a 36px gap, so the two links sat packed against the left
   while the text above them ran in two wide columns: nothing lined up with anything.
   A grid of the same two columns, with each block's OWN gutter (40px for a two
   column list, 44px for a run-on) and each block's OWN breakpoint back to one
   column, so the links fold at the same width the text above them does. */
.plain.two + .go-row,
.runon + .go-row{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)) }
.plain.two + .go-row{ column-gap:40px }
.runon + .go-row{ column-gap:44px }
.plain.two + .go-row > li,
.runon + .go-row > li{ min-width:0 }
/* ON THE TEXT, NOT ON THE BULLET. Steve, 2026-09-17: "Change the green link alignment to be
   with the text, not the bullets." Every `ul.plain li` sets `padding-left:18px` and hangs its
   dash in that space, so a link sitting flush with the column edge lines up with the dashes
   above it and not with a single word of the copy. The same 18px puts it under the text.
   The run-on paragraphs have no marker and are already flush, so this is not applied there. */
.plain.two + .go-row > li{ padding-left:18px }

/* AND WHERE THERE ARE NO COLUMNS TO LINE UP WITH, the pair spreads to the two edges of
   whatever is above it, which on the front page is a notice box running the full width.
   Steve: "please right align the second link so there's some symmetry." Only for a row of
   exactly two: a single link has nothing to be symmetrical with, and a row of three spread
   to the edges reads as three separate things rather than as a set. The column-aligned rows
   above are grids whose tracks already fill the width, so this does nothing to them. */
.go-row:has(> :nth-child(2)):not(:has(> :nth-child(3))){ justify-content:space-between }
/* EXCEPT IN A HERO, where they stack, one per line, left aligned, at Steve's ask 2026-09-17.
   Spreading them across the column had put the second link a clear 350px past the end of the
   paragraph it belongs to, because the hero copy is capped at 58 characters and the page column
   is not: a stray link rather than symmetry. Stacked, each one starts where the paragraph above
   it starts.
   THE 15px IS BETWEEN THE WORDS, NOT BETWEEN THE BOXES. Each link carries 9px of invisible
   padding above and below so that a thumb has something to hit, so a 15px row-gap would read as
   33px of space. The padding is trimmed to 7.5px here and the gap set to zero, which leaves
   exactly 15px of clear air and still gives a 34px target. */
.hero .go-row{ flex-direction:column; align-items:flex-start; justify-content:flex-start; row-gap:0 }
.hero .go-row a{ padding:7.5px 0 }
@media (max-width:820px){ .runon + .go-row{ grid-template-columns:minmax(0,1fr) } }
@media (max-width:760px){ .plain.two + .go-row{ grid-template-columns:minmax(0,1fr) } }
