/* pdf-assembler.com — one stylesheet for every page.
 *
 * No web fonts, no CDN, no analytics, no third-party anything. That is not
 * minimalism for its own sake: this site's whole argument is that the product does
 * not send your documents to somebody else's server, and a page that quietly loads
 * a font from Google while making that argument is not one to defend. It also means
 * the page renders instantly and works offline once cached.
 */

:root {
    /* The app's brand blue is #007AFF, and it stays that in the app icon and in the
       screenshots. It is not the link colour, because on white it measures 4.02:1 —
       under the 4.5:1 AA floor for body text, and the resting state was the failing
       one while :hover passed. Light mode therefore uses the same blue as the filled
       controls, 5.19:1, which is two blues in this scheme instead of four. */
    --accent: #0069DB;
    --accent-hover: #0057BF;
    /* Filled controls get their own blue. `--accent` is also the link colour, so in
       dark mode it has to be light enough to read on a dark page — and white text on
       that measures 2.65:1, which fails WCAG AA even for large text. This one is
       fixed across both schemes because white-on-blue does not care what is behind
       the button: 5.19:1 with white, and still the same blue to look at. Anything
       that puts white ON a blue must use this token, never --accent. */
    --accent-solid: #0069DB;
    --accent-solid-hover: #0057BF;
    --bg: #ffffff;
    --bg-soft: #f5f7fa;
    --bg-sunken: #eef2f7;
    --fg: #10151c;
    --fg-muted: #5b6673;
    --hairline: #e2e8f0;
    --shadow: 0 1px 2px rgba(16, 21, 28, .05), 0 8px 24px rgba(16, 21, 28, .06);
    --radius: 14px;
    --measure: 62ch;
}

@media (prefers-color-scheme: dark) {
    :root {
        --accent: #4da2ff;
        --accent-hover: #7cbaff;
        --bg: #0e1116;
        --bg-soft: #161b22;
        --bg-sunken: #1c222b;
        --fg: #e7edf4;
        --fg-muted: #9aa7b5;
        --hairline: #262d38;
        --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .35);
    }
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--fg);
    font: 17px/1.65 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, "Noto Sans", sans-serif;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { line-height: 1.2; letter-spacing: -.02em; margin: 0 0 .5em; }
h1 { font-size: clamp(2.1rem, 6vw, 3.4rem); font-weight: 800; }
h2 { font-size: clamp(1.5rem, 4vw, 2.1rem); font-weight: 700; }
h3 { font-size: 1.05rem; font-weight: 650; }
p { margin: 0 0 1em; }
a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent-hover); }
img { max-width: 100%; height: auto; display: block; }
small { font-size: .84rem; }

code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .92em;
    /* The privacy page names two settings paths — ~/Library/Containers/com.bhgmc.pdfassembler
       and ~/Library/Preferences/com.bhgmc.pdfassembler.plist — and `.` offers no
       break opportunity, so a bundle id is one unbreakable run. At 31 characters
       (the old com.benjamincarrel.pdfassembler) that was wider than a 320px phone's
       text column at the body's 17px, and it put a horizontal scrollbar on the
       whole page. The bhgmc id is 22, and 28 with `.plist`; the rule stays because
       it is the only thing guarding those paths, and `anywhere` also lowers the
       element's min-content width, so it can never widen its container again. */
    overflow-wrap: anywhere;
}

/* Off-screen but present, for text that names a control to a screen reader where the
   visible design already names it to everyone else. Not `display: none`, which would
   take it out of the accessibility tree along with everything else. */
.vh {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.wrap { max-width: 1040px; margin: 0 auto; padding: 0 24px; }
.narrow { max-width: var(--measure); }
.center { text-align: center; }
.muted { color: var(--fg-muted); }

section { padding: 72px 0; border-top: 1px solid var(--hairline); }
section:first-of-type { border-top: 0; }

/* ---------------------------------------------------------------- header --- */

.topbar {
    display: flex; align-items: center; gap: 12px;
    padding: 18px 24px;
    max-width: 1040px; margin: 0 auto;
    font-size: .95rem;
}
.topbar .home { font-weight: 700; color: var(--fg); text-decoration: none; letter-spacing: .02em; }
.topbar nav { margin-left: auto; display: flex; gap: 20px; }
.topbar nav a { color: var(--fg-muted); text-decoration: none; }
.topbar nav a:hover { color: var(--fg); }

/* ------------------------------------------------------------------ hero --- */

/* The hero is `<div class="hero wrap">`, one element with two classes, and `.wrap`
   owns the side gutter. So only the vertical padding is set here: the shorthand
   used to zero the sides as well, and on a phone the headline, the lede and the
   "coming soon" pill ran to the edge of the screen. */
.hero { padding-block: 56px 64px; text-align: center; }
.hero .appicon { width: 96px; height: 96px; margin: 0 auto 22px; border-radius: 22px; }
.hero .eyebrow {
    font-size: .95rem; font-weight: 650; letter-spacing: .06em;
    text-transform: uppercase; color: var(--fg-muted); margin-bottom: 14px;
}
.hero .lede { font-size: clamp(1.05rem, 2.4vw, 1.28rem); color: var(--fg-muted); margin: 0 auto 32px; max-width: 48ch; }

/* ------------------------------------------------------------------ demo --- */
/* The clip right under the hero. aspect-ratio as well as the width/height attributes,
   because `height: auto` on a <video> does not take its ratio from them until the
   metadata arrives in every browser, and the section below would jump when it does. */
.demo { margin: 0 auto; padding-bottom: 56px; }
.demo-clip, .demo-still {
    display: block; width: 100%; max-width: 100%; height: auto; aspect-ratio: 16 / 9;
    border-radius: var(--radius); box-shadow: var(--shadow); background: var(--bg-soft);
}
.demo figcaption { margin-top: 14px; text-align: center; font-size: .95rem; color: var(--fg-muted); }
.demo-still, .demo-link { display: none; }
/* No motion asked for: no clip. The poster frame stands in as an ordinary image, and
   the clip is one deliberate click away. */
@media (prefers-reduced-motion: reduce) {
    .demo-clip { display: none; }
    .demo-still { display: block; }
    .demo-link { display: inline; }
}

/* ---------------------------------------------------------------- stores --- */

.stores { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 18px; }

.store {
    display: inline-flex; align-items: center; gap: 10px;
    padding: 13px 22px; border-radius: 11px;
    font-weight: 650; font-size: 1rem; text-decoration: none;
    background: var(--accent-solid); color: #fff;
    border: 1px solid transparent;
    transition: background .15s ease;
}
.store:hover { background: var(--accent-solid-hover); color: #fff; }

/* The store's own badge, not a button of ours: fixed artwork, shown as supplied.
   Sized by height so its aspect ratio is never touched, and left without a hover
   effect on purpose — recolouring or fading a badge counts as altering it. The
   height matches the pending pill beside it so the row reads as one line. */
.badge { display: inline-flex; align-items: center; }
.badge img { display: block; height: 50px; width: auto; }

.store.pending {
    background: transparent; color: var(--fg-muted);
    border-color: var(--hairline); cursor: default;
}
.store.pending:hover { background: transparent; color: var(--fg-muted); }

.price { font-size: .95rem; color: var(--fg-muted); }

/* ----------------------------------------------------------------- shots --- */

/* Platform switch with no JavaScript: two radios drive which grid is visible.
   Without a script this still works, which is the point — the page has no script
   at all and therefore nothing that can fail to load.

   It is a <fieldset> so the pair has a name in the accessibility tree; these three
   lines undo what a fieldset brings with it. `min-width: 0` matters more than it
   looks: a fieldset's default `min-width: min-content` ignores the grid inside it and
   would let a wide screenshot push the page sideways. */
.shots { border: 0; margin: 0; padding: 0; min-width: 0; }
.shots input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }

.switch { display: flex; justify-content: center; gap: 6px; margin: 0 0 32px; }
.switch label {
    padding: 8px 20px; border-radius: 999px; cursor: pointer;
    font-size: .95rem; font-weight: 600; color: var(--fg-muted);
    background: var(--bg-sunken); border: 1px solid transparent;
    user-select: none;
}
.switch label:hover { color: var(--fg); }
#plat-win:checked ~ .switch label[for="plat-win"],
#plat-mac:checked ~ .switch label[for="plat-mac"] { background: var(--accent-solid); color: #fff; }

/* The radios are the only focusable controls on the site and they are invisible, so
   without this a keyboard user tabs into the screenshot switch and nothing at all
   moves. The ring goes on the label, because that is what is on screen. */
#plat-win:focus-visible ~ .switch label[for="plat-win"],
#plat-mac:focus-visible ~ .switch label[for="plat-mac"] {
    outline: 2px solid var(--accent-solid);
    outline-offset: 2px;
}

/* Windows High Contrast turns off author backgrounds and text colours, and the
   selected pill was distinguished by nothing else — both would have looked identical,
   on the platform whose store this ships to first. System colour keywords are the one
   thing forced-colors honours, so the state survives. */
@media (forced-colors: active) {
    #plat-win:checked ~ .switch label[for="plat-win"],
    #plat-mac:checked ~ .switch label[for="plat-mac"] {
        background: Highlight; color: HighlightText; border-color: Highlight;
    }
}

.grid-win, .grid-mac { display: none; }
#plat-win:checked ~ .grid-win { display: grid; }
#plat-mac:checked ~ .grid-mac { display: grid; }

/* One per row, not a two-up grid. The store screenshots are compositions — a
   headline above a picture of the window — so at half the container width the
   window itself lands around 250px and nothing in the app is legible. Better a
   longer page than four illustrations nobody can read. */
.grid-win, .grid-mac {
    grid-template-columns: 1fr;
    gap: 28px;
}
.grid-win img, .grid-mac img {
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    background: var(--bg-soft);
}

/* ----------------------------------------------------------------- cards --- */

.cards {
    display: grid; gap: 20px;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    margin-top: 36px;
}
.card {
    background: var(--bg-soft);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    padding: 22px 22px 18px;
}
.card h3 { margin-bottom: .35em; }
.card p { margin: 0; font-size: .95rem; color: var(--fg-muted); }

.steps { counter-reset: step; }
.steps .card h3::before {
    counter-increment: step; content: counter(step);
    display: inline-grid; place-items: center;
    width: 24px; height: 24px; margin-right: 9px; border-radius: 7px;
    /* --accent-solid, not --accent: this is white text on a blue fill at 13.6px
       bold, so it needs 4.5:1. On --accent it was 4.02:1 in light and 2.65:1 in
       dark — the exact number the comment at the top of this file was written
       about, in the one place that had not been changed to match it. */
    background: var(--accent-solid); color: #fff; font-size: .8rem; font-weight: 700;
    vertical-align: 2px;
}

/* ---------------------------------------------------------------- privacy -- */

.claims { list-style: none; padding: 0; margin: 28px 0 0; display: grid; gap: 14px; }
.claims li { display: flex; gap: 12px; align-items: flex-start; }
.claims li::before {
    content: ""; flex: none; margin-top: 7px;
    width: 15px; height: 9px;
    border-left: 2.5px solid var(--accent); border-bottom: 2.5px solid var(--accent);
    transform: rotate(-45deg);
}

/* ------------------------------------------------------------------ misc --- */

.facts { width: 100%; border-collapse: collapse; margin-top: 28px; font-size: .95rem; }
.facts th, .facts td { text-align: left; padding: 12px 0; border-bottom: 1px solid var(--hairline); vertical-align: top; }
.facts th { font-weight: 650; width: 34%; padding-right: 20px; }
.facts td { color: var(--fg-muted); }

/* The three-column comparison. Same bones as .facts; the first column is the
   aspect, the second the typical online tool, the third this app. */
.compare { width: 100%; border-collapse: collapse; margin-top: 28px; font-size: .95rem; }
.compare th, .compare td { text-align: left; padding: 12px 10px 12px 0; border-bottom: 1px solid var(--hairline); vertical-align: top; }
.compare thead th { font-weight: 650; color: var(--fg); }
.compare tbody th { font-weight: 650; width: 30%; padding-right: 20px; }
.compare td { color: var(--fg-muted); width: 35%; }

/* FAQ: native disclosure, no script. */
.faq { margin-top: 28px; display: grid; gap: 4px; }
.faq details { border-bottom: 1px solid var(--hairline); padding: 4px 0 14px; }
.faq summary { font-weight: 650; cursor: pointer; padding: 10px 0 6px; }
.faq p { margin: 8px 0 0; color: var(--fg-muted); }

/* A real list, so the eight names are eight things with CSS off as well as on — as
   bare spans with no whitespace between them they ran together into one string. */
.langs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 24px; list-style: none; padding: 0; }
.langs li {
    padding: 6px 13px; border-radius: 999px; font-size: .9rem;
    background: var(--bg-sunken); color: var(--fg-muted);
}

footer {
    border-top: 1px solid var(--hairline);
    padding: 36px 0 56px;
    font-size: .9rem; color: var(--fg-muted);
}
footer .wrap { display: flex; flex-wrap: wrap; gap: 10px 24px; align-items: center; }
footer a { color: var(--fg-muted); }
footer .spacer { margin-left: auto; }

/* ------------------------------------------------- what would have been inline --- */
/* The site sends `Content-Security-Policy: … style-src 'self'` (see `_headers`), and
   that blocks inline `style=` attributes. Cloudflare applies those headers; a local
   `python3 -m http.server` does not — so a page styled inline looks right in every
   test and arrives cramped in production, with nothing in the page to explain it.
   Fifteen attributes used to live in the HTML. They live here now. */
.lede-lg { font-size: 1.15rem; }
.note { margin-top: 28px; }
.section-gap { margin-top: 56px; }
.stores-gap { margin-top: 26px; }

.legal h1 { font-size: clamp(1.9rem, 5vw, 2.6rem); }
.legal h2 { margin-top: 44px; }
.legal ol { padding-left: 1.4em; margin: 0 0 1em; }
.legal li { margin-bottom: .5em; }

@media (max-width: 640px) {
    section { padding: 56px 0; }
    .hero { padding-block: 32px 48px; }
    .store { width: 100%; justify-content: center; }
    .badge { width: 100%; justify-content: center; }
    /* A phone cannot always hold the bar in one row. Before `flex-wrap` it
       overflowed a 320px screen and put a horizontal scrollbar on the whole page;
       now whatever does not fit drops to a second line instead, a longer word in
       another language included. The trimmed padding and link gap decide how
       often that happens: they keep the pages with two links or fewer on one row
       at 320px, and the product page, whose four links need more, from about
       414px up. Below that its nav sits on its own line under the wordmark, which
       is the intended phone layout. Those widths were measured with a stand-in
       for the system font, so take them as approximate. */
    .topbar { flex-wrap: wrap; padding: 14px 16px; }
    .topbar nav { gap: 14px; }
    /* The hero headline's line break is set for wide screens, after "never". On a
       phone "Your documents never" does not fit one line, so the break left
       "never" alone on a line of its own; here the headline wraps by itself,
       balanced into even lines instead. */
    .hero h1 br { display: none; }
    .hero h1 { text-wrap: balance; }
}
