/* The public site — home page and the four document pages.
 *
 * Hand-written rather than a third Tailwind build, and separate from
 * auth.css, for two reasons. The marketing pages carry their own palette,
 * type scale and typefaces — a system apart from the app's — and keeping
 * them out of auth.css means the login and signup templates are never
 * touched by a change here.
 *
 * bimdump ships LIGHT ONLY. There is no dark block anywhere in this file and
 * there must not be one: the bases declare `color-scheme: light` twice over
 * (see base_site.html), and a `prefers-color-scheme` rule here would fight
 * a decision made deliberately for the whole product.
 */

/* --- typefaces ------------------------------------------------------------
 * Self-hosted, committed, never a CDN at runtime — the rule the app already
 * follows for Plyr and Leaflet, and the one that keeps "no third-party
 * requests" true on a page that says so in its own privacy policy.
 * Refreshed from @fontsource by `just site-fonts`.
 */
@font-face {
    font-family: 'Bricolage Grotesque';
    src: url('fonts/bricolage-grotesque-latin-wght.woff2') format('woff2');
    font-weight: 200 800;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: 'Public Sans';
    src: url('fonts/public-sans-latin-400.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: 'Public Sans';
    src: url('fonts/public-sans-latin-600.woff2') format('woff2');
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

/* --- tokens ---------------------------------------------------------------
 * Blue is the only saturated colour on the site, and it is the app's own
 * `primary-600`: someone who signs up must not watch the colour change under
 * them. Everything else is achromatic, and there is no gradient anywhere —
 * that restraint is what keeps a stock blue from reading as generic chrome.
 */
:root {
    --paper: #f2f4f8;
    --sheet: #ffffff;
    --ink: #0b1b33;
    --blue: #2563eb;
    --blue-deep: #1d4ed8;
    --rule: #d5dce8;
    --muted: #55627a;

    --display: 'Bricolage Grotesque', 'Trebuchet MS', system-ui, sans-serif;
    --body: 'Public Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

    --measure: 36rem; /* ~65 characters at the body size */
    --gutter: 1.5rem;
}

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

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

body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--body);
    font-size: 1.125rem;
    line-height: 1.62;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

h1,
h2,
h3 {
    font-family: var(--display);
    font-weight: 600;
    line-height: 1.12;
    letter-spacing: -0.012em;
    margin: 0;
    text-wrap: balance;
}

p {
    margin: 0 0 1rem;
}

a {
    color: var(--blue);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}

a:hover {
    color: var(--blue-deep);
}

img {
    max-width: 100%;
    height: auto;
}

ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 3px;
    border-radius: 3px;
}

.wrap {
    max-width: 64rem;
    margin-inline: auto;
    padding-inline: var(--gutter);
}

/* Keyboard users reach the content without walking the nav first. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    background: var(--ink);
    color: var(--sheet);
    padding: 0.6rem 1rem;
    border-radius: 0 0 8px 0;
    z-index: 10;
}

.skip-link:focus {
    left: 0;
    color: var(--sheet);
}

/* --- the lockup ----------------------------------------------------------
 * The funnel mark is SVG; the word is real text in the display face. Drawing
 * the word as paths too would cost a second asset and a rebuild every time
 * the type changes, and would stop it being selectable.
 */
.lockup {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--ink);
    font-family: var(--display);
    font-weight: 600;
    font-size: 1.3rem;
    letter-spacing: -0.02em;
    text-decoration: none;
}

.lockup svg {
    width: 1.35em;
    height: 1.35em;
    color: var(--blue);
    flex: none;
}

.lockup:hover {
    color: var(--ink);
}

/* --- header --------------------------------------------------------------- */
.site-header {
    border-bottom: 1px solid var(--rule);
    background: var(--sheet);
}

.site-header .wrap {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-height: 4.25rem;
}

.site-nav {
    display: flex;
    align-items: center;
    gap: 1.4rem;
}

.site-nav a {
    color: var(--muted);
    text-decoration: none;
    font-size: 1.02rem;
}

.site-nav a:hover {
    color: var(--ink);
}

.site-nav .button {
    color: var(--sheet);
}

/* --- buttons -------------------------------------------------------------- */
.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.6rem 1.15rem;
    border-radius: 8px;
    background: var(--blue);
    color: var(--sheet);
    font-family: var(--display);
    font-weight: 600;
    font-size: 1.06rem;
    text-decoration: none;
    white-space: nowrap;
    border: 1px solid var(--blue);
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.button:hover {
    background: var(--blue-deep);
    border-color: var(--blue-deep);
    color: var(--sheet);
}

.button.quiet {
    background: transparent;
    color: var(--blue);
    border-color: var(--rule);
}

.button.quiet:hover {
    background: var(--sheet);
    border-color: var(--blue);
    color: var(--blue-deep);
}

/* --- footer --------------------------------------------------------------- */
.site-footer {
    border-top: 1px solid var(--rule);
    background: var(--sheet);
    padding-block: 2.25rem 2.75rem;
}

.site-footer .top {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.25rem;
}

.footer-links {
    display: flex;
    flex-wrap: wrap;
    column-gap: 1.4rem;
    row-gap: 0.5rem;
}

.footer-links a {
    color: var(--muted);
    font-size: 1rem;
    text-decoration: none;
}

.footer-links a:hover {
    color: var(--ink);
    text-decoration: underline;
}

.site-footer .legal {
    margin: 1.4rem 0 0;
    color: var(--muted);
    font-size: 0.93rem;
    max-width: 46rem;
}

/* --- the hero -------------------------------------------------------------
 * The page's one bold move: a scattered band of real captures narrowing
 * through a funnel into a single line. It is the app icon's shape and the
 * product's whole thesis, so everything below it stays deliberately quiet.
 */
.hero {
    position: relative;
    overflow: hidden;
    padding-block: 2.75rem 1.75rem;
    text-align: center;
}

.scraps {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.7rem;
    padding-inline: var(--gutter);
}

/* Each row is narrower than the one above it, so the pile tapers. The chip
   text was chosen for its length as much as its content — this is where the
   funnel comes from, and a longer line in the wrong row flattens it. */
.scrap-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.7rem 0.85rem;
}

.scrap {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    background: var(--sheet);
    border: 1px solid var(--rule);
    border-radius: 12px;
    padding: 0.5rem 0.95rem;
    font-size: 1.02rem;
    color: var(--ink);
    white-space: nowrap;
    rotate: var(--rot, 0deg);
}

.scrap svg {
    width: auto;
    height: 1rem;
    color: var(--blue);
    flex: none;
}

.scrap.voice svg {
    width: 2.9rem;
    height: 1.2rem;
}

/* NOT `.scrap.shot`: `.shot` is the filmstrip's card, and one class doing
   two jobs on one page is a rule that lands where nobody meant it to. The
   e2e strip assertions caught it — `.shot` resolved to nine hero chips. */
.scrap.link {
    color: var(--blue);
}

/* NO ENTRANCE ANIMATION, and that is a decision rather than an omission.
 *
 * The chips were staggered in on load, dropping downward — the product's own
 * gesture, and better than the stock fade-up. It came out with
 * `animation-fill-mode: both` and a per-row delay, which means every chip is
 * OPACITY 0 until its delay elapses. Anything that renders the page without
 * running animations to completion therefore sees an empty hero: Playwright's
 * `animations: "disabled"` does exactly that, which is what the screenshot
 * pipeline and the e2e tests use. Measured, not guessed — a full-page capture
 * came back with four chips missing.
 *
 * The taper already says the thing the motion was saying, so the motion is
 * the accessory that goes. It also means the hero renders identically in
 * every consumer, including a reduced-motion one, with no rule to maintain.
 */

.hero-words {
    margin-top: 1.5rem;
}

.hero h1 {
    font-size: clamp(2.1rem, 6.4vw, 3.5rem);
    letter-spacing: -0.025em;
}

.lede {
    max-width: var(--measure);
    margin-inline: auto;
    margin-top: 1.1rem;
    font-size: 1.22rem;
    color: var(--muted);
    text-wrap: balance;
}

/* `hidden` must actually hide. The attribute is only `display: none` in the
   UA stylesheet, so ANY author `display` beats it — and `.cta` sets one two
   rules down. That silently defeated the no-JavaScript case: the buttons
   ship hidden for site.js to reveal, and without this they were on screen
   the whole time, "Try without an account" included, pointing at an SPA
   that cannot boot without JavaScript. */
[hidden] {
    display: none !important;
}

.cta {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.7rem;
    margin-top: 1.75rem;
}

/* --- the filmstrip --------------------------------------------------------
 * Five phone-width screenshots of the real app, re-shot by `just shots`.
 * Full-bleed so the strip runs off both edges and fades, which is what says
 * "there is more of this" without an affordance that has to be explained.
 */
.strip {
    position: relative;
    margin-block: 0 2.75rem;
    --shot-w: clamp(206px, 25vw, 288px);
    --shot-h: calc(var(--shot-w) * 2.165);
    --shot-pad-top: 14px;
    --bezel: 6px;
}

.strip-stage {
    position: relative;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 11%, #000 89%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 11%, #000 89%, transparent);
}

.strip-track {
    display: flex;
    gap: 22px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;
    padding-block: var(--shot-pad-top) 4px;
    /* Half a viewport of padding either side is what lets the FIRST and LAST
       shot reach the middle; without it neither end can ever be active. */
    padding-inline: calc(50% - var(--shot-w) / 2);
}

.strip-track::-webkit-scrollbar {
    display: none;
}

.strip-track:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: -2px;
    border-radius: 14px;
}

.shot {
    flex: none;
    width: var(--shot-w);
    text-align: center;
    scroll-snap-align: center;
    transition: opacity 0.4s ease;
}

/* A dark bezel, because the screenshots are a light app on a light page and
   a hairline border was not enough to tell where one ended and the other
   began. It also reads as a phone, which is what these are. */
.shot-phone {
    display: block;
    width: 100%;
    height: var(--shot-h);
    padding: var(--bezel);
    overflow: hidden;
    border: 0;
    border-radius: 26px;
    background: var(--ink);
    transform-origin: bottom;
    transition: transform 0.4s ease, box-shadow 0.4s ease;
}

.shot-phone img {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 20px;
    object-fit: cover;
    object-position: top;
}

.can-zoom .shot-phone {
    cursor: zoom-in;
}

/* The dimming and the scale only exist once the script is driving the strip:
   with no JS nothing is "active", so all five must stay at full strength. */
.is-ready .shot {
    opacity: 0.55;
}

.is-ready .shot-phone {
    transform: scale(0.88);
}

.is-ready .shot.is-active {
    opacity: 1;
}

.is-ready .shot.is-active .shot-phone {
    transform: scale(1);
    box-shadow: 0 22px 44px -20px rgba(11, 27, 51, 0.55);
}

.shot-label {
    margin: 16px 0 0;
    font-family: var(--display);
    font-weight: 600;
    font-size: 1.06rem;
}

.shot-arrow,
.shot-modal-close {
    display: grid;
    place-items: center;
    width: 2.6rem;
    height: 2.6rem;
    border-radius: 999px;
    border: 1px solid var(--rule);
    background: var(--sheet);
    color: var(--blue);
    cursor: pointer;
    /* The shots are wide enough now that the arrows sit ON one. The shadow is
       what keeps them reading as controls floating above the strip rather
       than as part of the screenshot underneath. */
    box-shadow: 0 6px 18px -8px rgba(11, 27, 51, 0.55);
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.shot-arrow svg,
.shot-modal-close svg {
    width: 19px;
    height: 19px;
}

.shot-arrow {
    position: absolute;
    top: calc(var(--shot-pad-top) + var(--shot-h) / 2);
    transform: translateY(-50%);
}

.shot-arrow.prev {
    left: clamp(12px, 3vw, 56px);
}

.shot-arrow.next {
    right: clamp(12px, 3vw, 56px);
}

.shot-arrow:hover:not(:disabled),
.shot-modal-close:hover {
    border-color: var(--blue);
    background: var(--paper);
}

.shot-arrow:disabled {
    opacity: 0.35;
    cursor: default;
    color: var(--muted);
}

.shot-dots {
    display: flex;
    justify-content: center;
    gap: 0.25rem;
    margin-top: 14px;
}

.shot-dot {
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    border: 0;
    background: none;
    border-radius: 999px;
    cursor: pointer;
}

.shot-dot::before {
    content: '';
    display: block;
    width: 7px;
    height: 7px;
    border-radius: 999px;
    background: var(--muted);
    opacity: 0.4;
    transition: background-color 0.25s ease, opacity 0.25s ease, transform 0.25s ease;
}

.shot-dot[aria-current='true']::before {
    background: var(--blue);
    opacity: 1;
    transform: scale(1.4);
}

/* Hidden until the script can drive them — the arrows and dots do nothing
   without it, and an inert control is worse than an absent one. */
.strip:not(.is-ready) .shot-arrow,
.strip:not(.is-ready) .shot-dots {
    display: none;
}

.shot-modal {
    position: fixed;
    inset: 0;
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--ink);
}

.shot-modal::backdrop {
    background: rgba(11, 27, 51, 0.82);
}

.shot-modal figure {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    height: 100%;
    margin: 0;
    padding: 1rem;
}

.shot-modal img {
    display: block;
    width: auto;
    height: auto;
    max-width: calc(100vw - 40px);
    max-height: calc(100svh - 112px);
    padding: var(--bezel);
    border: 0;
    border-radius: 26px;
    background: var(--ink);
}

.shot-modal figcaption {
    font-family: var(--display);
    font-weight: 600;
    color: var(--sheet);
}

.shot-modal-close {
    position: absolute;
    top: 16px;
    right: 16px;
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* --- what goes in ---------------------------------------------------------
 * Rows separated by a rule, not cards. Four identical rounded boxes with the
 * same soft shadow is the shape every generated page reaches for; a hairline
 * says the same thing about grouping and says nothing else.
 */
.what {
    padding-block: 1rem 0.5rem;
}

.what h2,
.morning h2 {
    font-size: clamp(1.65rem, 3.9vw, 2.2rem);
}

.rows {
    display: grid;
    gap: 0 3rem;
    margin-top: 1.5rem;
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.rows li {
    border-top: 1px solid var(--rule);
    padding-block: 1.4rem;
}

.rows h3 {
    font-size: 1.16rem;
    margin-bottom: 0.35rem;
}

.rows p {
    color: var(--muted);
    margin: 0;
    max-width: 30rem;
}

/* --- the next morning ----------------------------------------------------- */
.morning {
    margin-top: 3.5rem;
    border-top: 1px solid var(--rule);
    background: var(--sheet);
    padding-block: 3rem 3.5rem;
}

/* Two columns, because one left-aligned column at reading measure inside a
   64rem container leaves half the band empty and the emptiness reads as a
   slot something was meant to go in. */
.morning .wrap {
    display: grid;
    grid-template-columns: 1.15fr 1fr;
    gap: 1.5rem 3.5rem;
    align-items: start;
}

.morning .cta {
    justify-content: flex-start;
}

.morning .lede,
.morning .aside {
    max-width: none;
    margin-inline: 0;
}

.morning .lede {
    color: var(--ink);
}

.morning .aside {
    margin-top: 0.5rem;
    color: var(--muted);
    font-size: 1.03rem;
}

/* --- document pages ------------------------------------------------------
 * Privacy, terms, support, delete account. One column at reading measure —
 * these are read, not scanned, and a wide legal page is a legal page nobody
 * finishes.
 */
.doc {
    max-width: calc(var(--measure) + 2 * var(--gutter));
    margin-inline: auto;
    padding-inline: var(--gutter);
    padding-block: 3rem 4rem;
}

.doc h1 {
    font-size: clamp(2rem, 6vw, 2.6rem);
}

.doc .updated {
    margin-block: 0.75rem 2.5rem;
    color: var(--muted);
    font-size: 0.98rem;
}

.doc h2 {
    font-size: 1.4rem;
    margin-block: 2.5rem 0.75rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--rule);
}

.doc h3 {
    font-size: 1.12rem;
    font-weight: 600;
    margin-block: 1.6rem 0.4rem;
}

.doc ul.bullets {
    list-style: disc;
    padding-left: 1.2rem;
    margin-bottom: 1rem;
}

.doc ul.bullets li {
    margin-bottom: 0.45rem;
}

/* An aside a reader must not skim past: the backup window, the fact that
 * deletion is final. Marked by a rule on the leading edge rather than a
 * tinted box, so it reads as emphasis and not as a decorative card. */
.doc .note {
    border-left: 3px solid var(--blue);
    background: var(--sheet);
    padding: 0.9rem 1.1rem;
    margin-block: 1.5rem;
    border-radius: 0 8px 8px 0;
}

.doc .note p:last-child {
    margin-bottom: 0;
}

@media (max-width: 46rem) {
    /* The taper has to survive a 390px screen, and four long chips in a row
       do not: they wrap, and a wrapped row is wider than the one above it,
       which is the one thing this shape cannot afford. Four chips step out
       and the rows keep descending. */
    .on-wide {
        display: none;
    }

    .scrap {
        font-size: 0.84rem;
        padding: 0.4rem 0.7rem;
    }

    .scrap-row {
        gap: 0.5rem 0.6rem;
    }

    .rows,
    .morning .wrap {
        grid-template-columns: 1fr;
    }

    .morning .cta {
        margin-top: 1.25rem;
    }

    .site-header .wrap {
        flex-wrap: wrap;
        padding-block: 0.85rem;
        gap: 0.6rem 1rem;
        min-height: 0;
    }

    .site-nav {
        gap: 1rem;
    }

    .shot-arrow {
        display: none;
    }

    /* Only the active shot keeps its label on a phone: five labels under five
       overlapping thumbnails is a row of unreadable fragments. */
    .is-ready .shot:not(.is-active) .shot-label {
        opacity: 0;
        transition: opacity 0.4s ease;
    }

    .shot-modal figure {
        padding-top: 64px;
    }

    .site-footer .top {
        flex-direction: column;
        align-items: flex-start;
    }
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition-duration: 0.01ms !important;
    }
}
