/* ==========================================================================
   client-intake.theme.css — the Client Intake Form, restyled for this site.
   Scoped `.in-app`. Load order: styles.css -> tool-base.css -> this file.
   Wrapper: <div class="tool-app in-app">

   WHAT THIS FILE REPLACES, AND WHY IT IS SHAPED LIKE THIS
   --------------------------------------------------------------------------
   The upstream form (webdesigner/client-intake) is a self-contained document
   that loads `https://cdn.tailwindcss.com` and two Google font families, then
   adds ~400 lines of hand-written component CSS on top. Neither CDN may ship
   here: an unpinned third-party script that can run arbitrary JS in this origin
   is the exact exposure the vendored `vendor/lucide.min.js` was created to
   close, and `.htaccess` Section 5 exists to keep the origin narrow. So the
   build script strips both, and this file supplies BOTH halves:

     LAYER 1  the ~140 Tailwind utilities the markup actually uses, written out
     LAYER 2  the ~38 component classes, ported to the site's dark/amber palette

   THIS FILE IS THE ONLY IMPLEMENTATION OF THOSE UTILITIES. That is a real
   coupling and it is the one thing to watch: if a future upstream build adds a
   class this file does not implement, the site copy silently loses that rule —
   no error, no console warning, just a paragraph with no margin. The build
   script therefore extracts every class from the generated HTML and diffs it
   against the set implemented here, and FAILS on an unknown one. Silent drift
   becomes a build error. Do not weaken that check to "warn"; the whole reason
   it exists is that nobody reads warnings from a generator that still emitted
   a file. Same argument as `build-cost-of-living.py`'s emoji gate.

   THE PALETTE IS INDIRECTED THROUGH `--in-*` ON PURPOSE.
   Every ported rule reads a `--in-*` variable rather than a site token
   directly, for the same reason `cost-of-living.theme.css` re-declares the
   planner's own names: the print block then re-maps ELEVEN variables instead of
   re-stating a hundred rules, and screen/paper cannot drift apart rule by rule.
   Do not "simplify" this by inlining site tokens into the rules.

   COLOUR DECISIONS THAT ARE NOT MECHANICAL (DESIGN.md is the authority):
   - Upstream's indigo `brand` ramp collapses to the site's single amber. There
     is no second decorative hue anywhere in this file.
   - `text-white` maps to DARK INK, not white. It occurs exactly once, on the
     agency-initial badge, which also carries `bg-brand-600`. Amber is a light
     accent: keeping the text white there would put white on #E5A344 (1.9:1).
   - Upstream carries nav-chip status as grey / amber / EMERALD. Three hues is
     two more than this site has. Status here is WEIGHT in one hue: ring ->
     half-filled -> filled. It also survives greyscale, which matters because
     this document's whole point is that it gets printed.
   - Red survives only where it is semantic: the required-field star, the
     invalid-field ring, the field error line, and the destructive hover on
     `.btn-quiet` (remove a repeater row). Never as decoration.
   ========================================================================== */

/* ==========================================================================
   0. PALETTE BRIDGE
   ========================================================================== */

.in-app {
    /* Ink ramp — upstream slate-900 / 700 / 600 / 500 */
    --in-ink: var(--color-text-primary);
    --in-dim: var(--color-text-secondary);
    --in-muted: var(--color-text-muted);

    /* Surfaces — upstream white / slate-50 / page */
    --in-card: var(--color-surface);
    --in-inset: var(--color-bg-tertiary);
    --in-page: var(--color-bg-primary);
    /* Floating layers (toast, review dialog). NOT --color-raised: that token is
       a tint *within* a card and sits 8 RGB units off the surface, which reads
       as a hole rather than a layer. styles.css says so where it defines it. */
    --in-overlay: var(--color-overlay);

    /* Hairlines — upstream slate-200 / slate-300 */
    --in-line: var(--color-border);
    --in-line-strong: var(--color-border-strong);

    /* The one accent — upstream brand-500/600/700/400/50/100 all land here */
    --in-accent: var(--color-primary);
    --in-accent-hi: var(--color-accent-light);
    --in-accent-lo: var(--color-primary-dark);
    --in-accent-tint: rgba(229, 163, 68, 0.10);
    --in-accent-tint2: rgba(229, 163, 68, 0.17);
    /* Text that sits ON the accent. See the header note. */
    --in-on-accent: #0A0A0B;
    --in-focus: rgba(229, 163, 68, 0.30);

    --in-error: var(--color-error);
    --in-error-tint: rgba(229, 72, 77, 0.16);

    /* `.navbar` is `position: fixed; z-index: 1000` and this form's own header
       is `position: sticky`. Both figures are MEASURED, not chosen — see the
       sticky-header rule near the bottom of Layer 2. */
    --in-nav-h: 71px;
    /* The form header's own height. JS keeps this current (its height changes
       with the section-nav row); the fallback is what a full header measures at
       desktop, so anchor jumps still clear it if the script never runs. */
    --in-head-h: 172px;

    color: var(--in-ink);
    font-family: var(--font-primary);
    font-size: 1rem;
    line-height: 1.5;
}

/* ==========================================================================
   1. LOCAL RESET
   Upstream got these from Tailwind's preflight, which we do not ship. Each is
   load-bearing; none is tidying.
   ========================================================================== */

/* THE MOST IMPORTANT RULE IN THIS FILE. The UA sheet's `[hidden]` is only
   `display: none`, so any element carrying `grid` or `flex` defeats it — and
   the conditional sections driven by `data-show-when` are exactly the elements
   carrying those. Without this, every conditional branch renders at once and
   the form asks questions it has already ruled out. Same trap logged for
   `classified.html`; assert COMPUTED DISPLAY in tests, never the property. */
.in-app [hidden] {
    display: none !important;
}

.in-app fieldset {
    border: 0;
    padding: 0;
    margin: 0;
    /* Without this a fieldset refuses to shrink below its content's intrinsic
       width and a long option label blows the grid column out. */
    min-width: 0;
}

.in-app legend {
    padding: 0;
}

/* styles.css styles headings and paragraphs for the site's editorial pages —
   its own display font, weights, colours and margins. Inside the tool those
   are set by `.display` and the text utilities, so neutralise the site's and
   let the tool's own layer speak. Without this, `h2.section-title` arrives
   pre-sized and the `text-2xl sm:text-3xl` pair on it does nothing. */
.in-app h1,
.in-app h2,
.in-app h3,
.in-app h4,
.in-app p {
    margin: 0;
    color: inherit;
    font-family: inherit;
    font-size: inherit;
    font-weight: inherit;
    letter-spacing: inherit;
    line-height: inherit;
}

.in-app button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    /* Kills the 300ms double-tap-zoom delay on touch. */
    touch-action: manipulation;
}

.in-app a {
    color: inherit;
    text-decoration: none;
}

.in-app input,
.in-app select,
.in-app textarea {
    font: inherit;
    color: inherit;
}

.in-app img,
.in-app svg {
    display: block;
    max-width: 100%;
}

.in-app .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ==========================================================================
   2. UTILITY LAYER  (the Tailwind classes the markup uses)

   Order inside this layer is a contract: WIDTH -> STYLE -> COLOUR for borders,
   because `.border` is a shorthand that resets colour and style, and the markup
   composes `border border-dashed border-slate-300` expecting the later two to
   win. Every selector here is (0,2,0) — `.in-app` plus one class — so file
   order is the only tiebreaker there is.

   `\.` and `\:` and `\/` escapes: the class NAMES contain those characters
   (`h-1.5`, `sm:flex`, `bg-white/95`). The escape is in the selector only; the
   markup is unchanged.
   ========================================================================== */

/* ---------- display / position ---------- */
.in-app .block        { display: block; }
.in-app .flex         { display: flex; }
.in-app .grid         { display: grid; }
.in-app .hidden       { display: none; }
.in-app .sticky       { position: sticky; }
.in-app .top-0        { top: 0; }
.in-app .z-40         { z-index: 40; }
.in-app .overflow-hidden { overflow: hidden; }
.in-app .flex-1       { flex: 1 1 0%; }
.in-app .flex-col     { flex-direction: column; }
.in-app .shrink-0     { flex-shrink: 0; }
.in-app .items-center { align-items: center; }
.in-app .justify-between { justify-content: space-between; }
.in-app .justify-center  { justify-content: center; }
.in-app .mx-auto      { margin-left: auto; margin-right: auto; }

/* `min-w-0` is not cosmetic: a flex item's automatic minimum size is its
   content, so a long client name in the header pushes the action buttons off
   screen without it. Paired with `.truncate` everywhere it appears. */
.in-app .min-w-0      { min-width: 0; }

.in-app .truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.in-app .whitespace-nowrap { white-space: nowrap; }

/* ---------- size ---------- */
.in-app .h-1          { height: 0.25rem; }
.in-app .h-1\.5       { height: 0.375rem; }
.in-app .h-9          { height: 2.25rem; }
.in-app .h-full       { height: 100%; }
.in-app .w-1\.5       { width: 0.375rem; }
.in-app .w-9          { width: 2.25rem; }
.in-app .max-w-2xl    { max-width: 42rem; }
.in-app .max-w-5xl    { max-width: 64rem; }

/* ---------- grid / gap ---------- */
.in-app .grid-cols-2  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.in-app .gap-2        { gap: 0.5rem; }
.in-app .gap-3        { gap: 0.75rem; }
.in-app .gap-4        { gap: 1rem; }
.in-app .gap-5        { gap: 1.25rem; }
.in-app .gap-x-3      { column-gap: 0.75rem; }
.in-app .gap-y-2      { row-gap: 0.5rem; }

/* ---------- margin ---------- */
.in-app .mb-0\.5      { margin-bottom: 0.125rem; }
.in-app .mb-1         { margin-bottom: 0.25rem; }
.in-app .mb-2         { margin-bottom: 0.5rem; }
.in-app .mb-3         { margin-bottom: 0.75rem; }
.in-app .mb-4         { margin-bottom: 1rem; }
.in-app .mb-5         { margin-bottom: 1.25rem; }
.in-app .mb-6         { margin-bottom: 1.5rem; }
.in-app .mb-7         { margin-bottom: 1.75rem; }
.in-app .mt-2         { margin-top: 0.5rem; }
.in-app .mt-3         { margin-top: 0.75rem; }
.in-app .mt-4         { margin-top: 1rem; }
.in-app .mt-5         { margin-top: 1.25rem; }
.in-app .mt-6         { margin-top: 1.5rem; }
.in-app .mt-7         { margin-top: 1.75rem; }
.in-app .mt-8         { margin-top: 2rem; }
.in-app .ml-0         { margin-left: 0; }
.in-app .ml-9         { margin-left: 2.25rem; }
.in-app .mr-1         { margin-right: 0.25rem; }
.in-app .mr-2         { margin-right: 0.5rem; }

/* Tailwind's `space-y-*` is a sibling rule, not a gap — it must stay one, or
   it would also space the first child away from the container edge. */
.in-app .space-y-3 > * + * { margin-top: 0.75rem; }
.in-app .space-y-6 > * + * { margin-top: 1.5rem; }

/* ---------- padding ---------- */
.in-app .pb-2         { padding-bottom: 0.5rem; }
.in-app .pb-3         { padding-bottom: 0.75rem; }
.in-app .pt-2         { padding-top: 0.5rem; }
.in-app .pt-5         { padding-top: 1.25rem; }
.in-app .pt-6         { padding-top: 1.5rem; }
.in-app .pt-8         { padding-top: 2rem; }
.in-app .px-3         { padding-left: 0.75rem; padding-right: 0.75rem; }
.in-app .px-4         { padding-left: 1rem; padding-right: 1rem; }
.in-app .py-2         { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.in-app .py-3         { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.in-app .py-8         { padding-top: 2rem; padding-bottom: 2rem; }
.in-app .py-10        { padding-top: 2.5rem; padding-bottom: 2.5rem; }

/* ---------- border: WIDTH ---------- */
.in-app .border       { border: 1px solid var(--in-line); }
.in-app .border-b     { border-bottom: 1px solid var(--in-line); }
.in-app .border-t     { border-top: 1px solid var(--in-line); }
.in-app .divide-y > * + * { border-top: 1px solid var(--in-line); }

/* ---------- border: STYLE (after width — see the layer note) ---------- */
.in-app .border-dashed { border-style: dashed; }

/* ---------- border: COLOUR (after style) ---------- */
.in-app .border-slate-200 { border-color: var(--in-line); }
.in-app .border-slate-300 { border-color: var(--in-line-strong); }
.in-app .border-amber-200 { border-color: var(--in-accent-tint2); }
.in-app .divide-slate-100 > * + * { border-top-color: var(--in-line); }

/* ---------- radius ---------- */
.in-app .rounded-lg   { border-radius: 0.5rem; }
.in-app .rounded-xl   { border-radius: 0.75rem; }
.in-app .rounded-full { border-radius: 9999px; }

/* ---------- background ---------- */
.in-app .bg-white      { background: var(--in-card); }
.in-app .bg-slate-50   { background: var(--in-inset); }
.in-app .bg-slate-100  { background: var(--in-line); }
.in-app .bg-slate-300  { background: var(--in-line-strong); }
.in-app .bg-brand-500  { background: var(--in-accent); }
.in-app .bg-brand-600  { background: var(--in-accent); }

/* The one notice box. Upstream is amber-on-amber, which on this site is simply
   the accent — so it stays a notice rather than becoming a second hue. */
.in-app .bg-amber-50 {
    background: var(--in-accent-tint);
    /* "Background graphics" is a print-dialog checkbox and it is OFF by default
       in Firefox's silent-print path. Without this the notice prints as bare
       text with a border and stops reading as a callout. */
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
}

/* The sticky header band. Opaque enough that form rows scrolling behind it do
   not read through; the blur is what keeps it from looking like a hard slab. */
.in-app .bg-white\/95 { background: rgba(18, 18, 20, 0.94); }
.in-app .backdrop-blur {
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}

/* ---------- type ---------- */
.in-app .text-xs   { font-size: 0.75rem;  line-height: 1rem; }
.in-app .text-sm   { font-size: 0.875rem; line-height: 1.25rem; }
.in-app .text-base { font-size: 1rem;     line-height: 1.5rem; }
.in-app .text-lg   { font-size: 1.125rem; line-height: 1.75rem; }
.in-app .text-xl   { font-size: 1.25rem;  line-height: 1.75rem; }
.in-app .text-2xl  { font-size: 1.5rem;   line-height: 2rem; }
.in-app .text-3xl  { font-size: 1.875rem; line-height: 2.25rem; }
.in-app .text-center { text-align: center; }
.in-app .leading-tight    { line-height: 1.25; }
.in-app .tracking-tight   { letter-spacing: -0.025em; }
.in-app .tracking-widest  { letter-spacing: 0.1em; }
.in-app .uppercase        { text-transform: uppercase; }
.in-app .underline        { text-decoration: underline; }
.in-app .font-medium      { font-weight: 500; }
.in-app .font-semibold    { font-weight: 600; }
.in-app .font-bold        { font-weight: 700; }
.in-app .font-extrabold   { font-weight: 800; }
.in-app .antialiased {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* ---------- type colour ---------- */
.in-app .text-slate-900 { color: var(--in-ink); }
.in-app .text-slate-600 { color: var(--in-dim); }
.in-app .text-slate-500 { color: var(--in-muted); }
.in-app .text-brand-600 { color: var(--in-accent); }
.in-app .text-brand-700 { color: var(--in-accent); }
.in-app .text-amber-900 { color: var(--in-accent-hi); }

/* DARK ink, not white — the single use sits on `bg-brand-600` (amber), and
   white on #E5A344 measures 1.9:1. See the header note. */
.in-app .text-white     { color: var(--in-on-accent); }

/* SITE-ONLY, not an upstream utility: the semantic red, for the one message
   that reports a failed send. Amber there would read as "accent", not as
   "this did not go through" — and this site spends amber on one job. */
.in-app .text-error     { color: var(--in-error); }

/* SITE-ONLY: the autosave indicator's dot. Upstream carries its three states
   as slate / EMERALD / slate-400 — a second hue this site does not have, and a
   scale that is three identical greys once the page is printed or read by
   anyone with a red-green deficiency. Same ring -> half -> filled language as
   the section-nav dots, in the one accent.

   The base (never saved) state is `bg-slate-300` in the markup; these two are
   assigned by the engine, and the build script rewrites its two colour
   literals to these names. If you rename one, rename it there too. */
.in-app .dot-idle       { background: var(--in-line-strong); }
.in-app .dot-live       { background: var(--in-accent); }

/* ---------- transition ---------- */
.in-app .transition {
    transition-property: color, background-color, border-color, fill, stroke,
        opacity, box-shadow, transform, filter, backdrop-filter;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    transition-duration: 150ms;
}
.in-app .transition-all {
    transition-property: all;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    transition-duration: 150ms;
}
.in-app .duration-300 { transition-duration: 300ms; }

/* ---------- hover ----------
   Every hover on this site sits inside `@media (hover: hover)`. Without the
   gate a tap fires `:hover` on a phone and it STICKS until the next tap
   elsewhere, which on a 27-section form means a trail of half-lit controls.
   Headless engines report `(hover: hover)` as false in BOTH Chromium and
   Firefox — they have no pointing device — so these rules are inert under the
   QA harnesses and must be verified under the Xvfb rig with a real browser. */
@media (hover: hover) {
    .in-app .hover\:border-brand-400:hover { border-color: var(--in-accent-hi); }
    .in-app .hover\:text-brand-700:hover   { color: var(--in-accent); }
    .in-app .hover\:text-slate-700:hover   { color: var(--in-ink); }
}

/* ---------- responsive: sm (640px) ---------- */
@media (min-width: 640px) {
    .in-app .sm\:flex         { display: flex; }
    .in-app .sm\:grid         { display: grid; }
    .in-app .sm\:hidden       { display: none; }
    .in-app .sm\:flex-1       { flex: 1 1 0%; }
    .in-app .sm\:flex-row     { flex-direction: row; }
    .in-app .sm\:items-center { align-items: center; }
    .in-app .sm\:grid-cols-2  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .in-app .sm\:grid-cols-3  { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .in-app .sm\:grid-cols-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); }
    .in-app .sm\:col-span-2   { grid-column: span 2 / span 2; }
    .in-app .sm\:col-span-3   { grid-column: span 3 / span 3; }
    .in-app .sm\:col-span-4   { grid-column: span 4 / span 4; }
    .in-app .sm\:ml-9         { margin-left: 2.25rem; }
    .in-app .sm\:px-6         { padding-left: 1.5rem; padding-right: 1.5rem; }
    .in-app .sm\:text-lg      { font-size: 1.125rem; line-height: 1.75rem; }
    .in-app .sm\:text-3xl     { font-size: 1.875rem; line-height: 2.25rem; }
    .in-app .sm\:text-5xl     { font-size: 3rem;     line-height: 1; }
}

/* ---------- responsive: lg (1024px) ---------- */
@media (min-width: 1024px) {
    .in-app .lg\:flex        { display: flex; }
    .in-app .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ==========================================================================
   3. COMPONENT LAYER  (the ported `05-styles.css` classes)
   ========================================================================== */

/* The site's display face replaces upstream's Fraunces. Weight 700 rather than
   600: Cabinet Grotesk runs lighter than Fraunces at the same nominal weight,
   and the section titles are the form's only navigational landmark on paper. */
.in-app .display {
    font-family: var(--font-display);
    font-weight: 700;
    letter-spacing: -0.02em;
}

.in-app .skip-link {
    position: absolute;
    left: 0.5rem;
    top: -4rem;
    z-index: 60;
    background: var(--in-accent);
    color: var(--in-on-accent);
    padding: 0.6rem 1rem;
    border-radius: var(--radius-lg);
    font-size: 0.9rem;
    font-weight: 600;
    transition: top 0.15s;
}
/* Lands BELOW the site navbar, not under it — the skip link is the one control
   a keyboard user meets first, and the navbar is fixed over the top 71px. */
.in-app .skip-link:focus {
    top: calc(var(--in-nav-h) + 0.5rem);
}

/* ---------- fields ---------- */

.in-app .field-input,
.in-app .field-textarea,
.in-app .field-select {
    width: 100%;
    padding: 0.625rem 0.875rem;
    border: 1px solid var(--in-line-strong);
    border-radius: var(--radius-lg);
    /* Inset, not raised: a field is a hole in the card, and this is the same
       pairing the other tools use (`background: var(--bg)` on a card). */
    background: var(--in-page);
    color: var(--in-ink);
    /* 16px, LITERAL, never rem. styles.css:1142 drops :root to 15px at 480px,
       so a rem-sized field renders at 15px on exactly the phones that matter —
       and below 16px iOS Safari zooms the viewport on focus and does not zoom
       back out. A 27-section form is the worst possible place for that. */
    font-size: 16px;
    min-height: 44px;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.in-app .field-input::placeholder,
.in-app .field-textarea::placeholder {
    color: var(--in-muted);
}

.in-app .field-input:focus,
.in-app .field-textarea:focus,
.in-app .field-select:focus {
    outline: none;
    border-color: var(--in-accent);
    box-shadow: 0 0 0 3px var(--in-focus);
}

.in-app .field-textarea {
    min-height: 5.5rem;
    resize: vertical;
    line-height: 1.5;
}

.in-app .field-label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--in-ink);
    margin-bottom: 0.35rem;
}

.in-app .field-help {
    font-size: 0.8rem;
    color: var(--in-muted);
    margin-top: 0.35rem;
}

/* Semantic red — a required marker, not decoration. */
.in-app .req::after {
    content: " *";
    color: var(--in-error);
}

.in-app .field-invalid,
.in-app .field-invalid:focus {
    border-color: var(--in-error);
    box-shadow: 0 0 0 3px var(--in-error-tint);
}

.in-app .field-error {
    font-size: 0.8rem;
    color: var(--in-error);
    margin-top: 0.35rem;
    font-weight: 500;
}

/* The "here is the field you missed" pulse after a failed review. Amber, and
   deliberately a box-shadow rather than a background change: the field may
   already be carrying the red invalid ring, and two colours fighting over one
   border is how a validation state stops being readable. */
@keyframes inFieldFlash {
    0%, 100% { box-shadow: 0 0 0 0 rgba(229, 163, 68, 0); }
    30%      { box-shadow: 0 0 0 5px rgba(229, 163, 68, 0.45); }
}
.in-app .field-flash { animation: inFieldFlash 1.4s ease-out; }

/* ---------- pickers ---------- */

.in-app .pick-tile {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.75rem 0.9rem;
    min-height: 44px;
    border: 1px solid var(--in-line);
    border-radius: 0.55rem;
    background: var(--in-card);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
    user-select: none;
}

.in-app .pick-tile input {
    margin-top: 0.2rem;
    /* Native control, tinted. The grocery list's checkbox rule, same reasoning:
       a real input toggles from its own label with no click handler to keep in
       sync, and it prints as a real tick box. */
    accent-color: var(--in-accent);
    flex-shrink: 0;
}

.in-app .pick-tile:has(input:checked) {
    border-color: var(--in-accent);
    background: var(--in-accent-tint);
}

.in-app .pick-tile:focus-within {
    border-color: var(--in-accent);
    box-shadow: 0 0 0 3px var(--in-focus);
}

.in-app .palette-tile {
    border: 1px solid var(--in-line);
    border-radius: 0.7rem;
    padding: 0.85rem;
    background: var(--in-card);
    cursor: pointer;
    transition: border-color 0.15s;
}

.in-app .palette-tile:has(input:checked),
.in-app .palette-tile:focus-within {
    border-color: var(--in-accent);
    box-shadow: 0 0 0 3px var(--in-focus);
}

.in-app .palette-swatches {
    display: flex;
    gap: 0.35rem;
    margin-top: 0.5rem;
}

/* The ONE place a hue other than amber is legitimate: these swatches ARE the
   data — the client is picking a colour, so the swatch must show the colour it
   names. Their fills are inline `style` attributes in the markup, not tokens.
   The hairline round them is ours and stays neutral. */
.in-app .palette-swatches span {
    width: 1.6rem;
    height: 1.6rem;
    border-radius: 50%;
    border: 1px solid var(--in-line-strong);
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
}

@media (hover: hover) {
    .in-app .pick-tile:hover,
    .in-app .palette-tile:hover { border-color: var(--in-line-strong); }
}

/* ---------- sections ---------- */

.in-app .section {
    background: var(--in-card);
    border: 1px solid var(--in-line);
    border-radius: 1rem;
    padding: 1.75rem;
    /* An anchor jump must clear BOTH fixed layers: the site navbar and this
       form's own sticky header. `--in-head-h` is kept current by JS; the
       fallback is a measured full-height header, so the jump still lands
       correctly if the script never runs. */
    scroll-margin-top: calc(var(--in-nav-h) + var(--in-head-h) + 12px);
}

@media (min-width: 768px) {
    .in-app .section { padding: 2.25rem 2.5rem; }
}

.in-app .section-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.85rem;
    height: 1.85rem;
    border-radius: 50%;
    background: var(--in-accent-tint2);
    color: var(--in-accent);
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: 0.85rem;
    margin-right: 0.65rem;
    flex-shrink: 0;
}

.in-app .section-title {
    display: flex;
    align-items: center;
}

/* ---------- buttons ---------- */

.in-app .btn-primary,
.in-app .btn-ghost,
.in-app .btn-quiet {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 0.55rem 1rem;
    border-radius: var(--radius-lg);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.in-app .btn-primary {
    background: var(--in-accent);
    color: var(--in-on-accent);
    border: 1px solid transparent;
}

.in-app .btn-ghost {
    background: var(--in-card);
    color: var(--in-ink);
    border: 1px solid var(--in-line);
    font-weight: 500;
}

.in-app .btn-quiet {
    background: transparent;
    color: var(--in-muted);
    border: 1px solid transparent;
    font-weight: 500;
    padding-left: 0;
    padding-right: 0.75rem;
}

@media (hover: hover) {
    .in-app .btn-primary:hover { background: var(--in-accent-hi); }
    .in-app .btn-ghost:hover {
        background: var(--in-inset);
        border-color: var(--in-line-strong);
    }
    /* Destructive: this is "remove this row". Semantic red. */
    .in-app .btn-quiet:hover { color: var(--in-error); }
}

/* `:active` is the replacement for the press feedback the hover gate removes on
   touch, not an extra. Without it a phone tap has no acknowledgement at all. */
.in-app .btn-primary:active,
.in-app .btn-ghost:active,
.in-app .btn-quiet:active { transform: translateY(1px); }

.in-app .btn-primary:focus-visible,
.in-app .btn-ghost:focus-visible,
.in-app .btn-quiet:focus-visible {
    outline: 2px solid var(--in-accent);
    outline-offset: 2px;
}

.in-app .segmented {
    display: inline-flex;
    border: 1px solid var(--in-line);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--in-card);
}

.in-app .segmented button {
    min-height: 2.25rem;
    padding: 0.3rem 0.7rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--in-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.in-app .segmented button + button { border-left: 1px solid var(--in-line); }

/* The selected segment. `lang-on` is the class the language and depth switches
   both use — it is not language-specific despite the name. */
.in-app .segmented button.lang-on {
    background: var(--in-accent);
    color: var(--in-on-accent);
}

@media (hover: hover) {
    .in-app .segmented button:hover { background: var(--in-inset); }
    .in-app .segmented button.lang-on:hover { background: var(--in-accent-hi); }
}

.in-app .segmented button:focus-visible {
    outline: 2px solid var(--in-accent);
    outline-offset: -2px;
}

/* ---------- delivery choices ---------- */

.in-app .action-card {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    text-align: left;
    padding: 0.9rem 1.1rem;
    border: 1px solid var(--in-line);
    border-radius: 0.7rem;
    background: var(--in-card);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
}

.in-app .action-card:focus-visible {
    outline: 2px solid var(--in-accent);
    outline-offset: 2px;
}

.in-app .action-card-title {
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--in-ink);
}

.in-app .action-card-sub {
    font-size: 0.8rem;
    color: var(--in-muted);
    line-height: 1.45;
}

.in-app .action-card-primary {
    border-color: var(--in-accent);
    background: var(--in-accent-tint);
}

@media (hover: hover) {
    .in-app .action-card:hover {
        border-color: var(--in-line-strong);
        background: var(--in-inset);
    }
    .in-app .action-card-primary:hover {
        background: var(--in-accent-tint2);
        border-color: var(--in-accent-hi);
    }
}

/* ---------- repeaters ---------- */

.in-app .repeater-row {
    border: 1px solid var(--in-line);
    border-radius: 0.7rem;
    padding: 1rem;
    /* Room for the absolutely-positioned remove button. The touch block below
       grows both together — they are one measurement, not two. */
    padding-right: 2.75rem;
    background: var(--in-inset);
    position: relative;
}

.in-app .repeater-remove {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background: var(--in-card);
    border: 1px solid var(--in-line);
    color: var(--in-muted);
    font-size: 1rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
}

@media (hover: hover) {
    .in-app .repeater-remove:hover {
        color: var(--in-error);
        border-color: var(--in-error);
    }
}

.in-app .repeater-remove:focus-visible {
    outline: 2px solid var(--in-accent);
    outline-offset: 2px;
}

.in-app .repeater-add {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    cursor: pointer;
}

/* ---------- section nav ---------- */

.in-app .nav-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8rem;
    font-weight: 500;
    padding: 0.4rem 0.75rem;
    border-radius: 9999px;
    border: 1px solid var(--in-line);
    color: var(--in-dim);
    background: var(--in-card);
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}

@media (hover: hover) {
    .in-app .nav-chip:hover {
        border-color: var(--in-accent-hi);
        color: var(--in-ink);
    }
}

.in-app .nav-chip:focus-visible {
    outline: 2px solid var(--in-accent);
    outline-offset: 2px;
}

.in-app .nav-chip[aria-current] {
    background: var(--in-accent);
    color: var(--in-on-accent);
    border-color: var(--in-accent);
}

/* STATUS IS WEIGHT, NOT HUE — ring, half, filled, all in the one accent.
   Upstream is grey / amber / EMERALD, which is two hues this site does not
   have and a scale that collapses in greyscale. This document gets printed:
   a reader holding the paper version has to be able to tell a finished section
   from a started one, and a green dot and an amber dot are the same grey. */
.in-app .chip-dot {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    border: 1.5px solid var(--in-line-strong);
    background: transparent;
    flex-shrink: 0;
    box-sizing: content-box;
}

.in-app .nav-chip[data-state="partial"] .chip-dot {
    border-color: var(--in-accent);
    background: var(--in-accent-tint2);
}

.in-app .nav-chip[data-state="complete"] .chip-dot {
    border-color: var(--in-accent);
    background: var(--in-accent);
}

/* On the CURRENT chip the ground is amber, so an amber dot would vanish into
   it. The same three weights, inverted onto ink. */
.in-app .nav-chip[aria-current] .chip-dot {
    border-color: rgba(10, 10, 11, 0.45);
    background: transparent;
}
.in-app .nav-chip[aria-current][data-state="partial"] .chip-dot {
    border-color: var(--in-on-accent);
    background: rgba(10, 10, 11, 0.4);
}
.in-app .nav-chip[aria-current][data-state="complete"] .chip-dot {
    border-color: var(--in-on-accent);
    background: var(--in-on-accent);
}

.in-app .nav-scroll {
    overflow-x: auto;
    scrollbar-width: thin;
    /* Gecko needs this explicitly. `color-scheme: dark` alone left the tools'
       horizontal scrollbars drawing a solid WHITE bar across a dark card —
       a Firefox-only defect, logged, and this strip is the same shape. */
    scrollbar-color: var(--in-line-strong) transparent;
}
.in-app .nav-scroll::-webkit-scrollbar { height: 6px; }
.in-app .nav-scroll::-webkit-scrollbar-thumb {
    background: var(--in-line-strong);
    border-radius: 3px;
}

/* ---------- review dialog ---------- */

/* A `<dialog>` takes its ground from the UA sheet (`canvas`), and with
   `color-scheme: dark` at the root that is already dark — but it is the OS's
   dark, not ours, so it must be stated. This is a floating layer, hence
   `--in-overlay` rather than the card colour. */
.in-app .review-dialog {
    max-width: 30rem;
    width: calc(100% - 2rem);
    background: var(--in-overlay);
    border: 1px solid var(--in-line-strong);
    border-radius: 1rem;
    padding: 1.5rem;
    box-shadow: var(--shadow-lg);
    color: var(--in-ink);
}

.in-app .review-dialog::backdrop {
    background: rgba(10, 10, 11, 0.7);
}

.in-app .review-list {
    max-height: 40vh;
    overflow-y: auto;
    display: grid;
    gap: 0.35rem;
    scrollbar-color: var(--in-line-strong) transparent;
}

.in-app .review-item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    width: 100%;
    text-align: left;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--in-line);
    border-radius: var(--radius-lg);
    background: var(--in-card);
    font-size: 0.875rem;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
}

@media (hover: hover) {
    .in-app .review-item:hover {
        border-color: var(--in-accent);
        background: var(--in-accent-tint);
    }
}

.in-app .review-item:focus-visible {
    outline: 2px solid var(--in-accent);
    outline-offset: 2px;
}

/* ---------- toast ---------- */

/* Upstream's toast is slate-900 on a white page. On a near-black page that is
   invisible, so it moves UP to the overlay token and takes a hairline. */
.in-app .toast {
    position: fixed;
    bottom: 1.25rem;
    left: 50%;
    transform: translateX(-50%) translateY(140%);
    background: var(--in-overlay);
    border: 1px solid var(--in-line-strong);
    color: var(--in-ink);
    padding: 0.75rem 1.25rem;
    border-radius: 0.7rem;
    font-size: 0.9rem;
    font-weight: 500;
    max-width: calc(100% - 2rem);
    box-shadow: var(--shadow-lg);
    transition: transform 0.25s ease;
    /* Above everything the tool draws, and still below the site navbar's 1000
       — the toast is bottom-anchored, so it never contends with it anyway. */
    z-index: 100;
}

.in-app .toast.show { transform: translateX(-50%) translateY(0); }

/* Screen-only: the printed identification band. It is in normal flow rather
   than a fixed running header BY DESIGN — Chrome positions fixed elements
   against the first page's viewport and repeats them there, which drops the
   stamp on top of whatever occupies that spot on later sheets. */
.in-app .print-stamp { display: none; }

/* ==========================================================================
   4. THE STICKY FORM HEADER

   THIS IS A CORRECTNESS RULE, NOT POLISH — the same class of defect that made
   the grocery list untappable on a phone for a day.

   `.navbar` is `position: fixed; z-index: 1000`, painted `rgba(10,10,11,0.55)`
   over a 14px blur, and 71px tall at this root font-size. The form's header is
   `position: sticky; top: 0` upstream, so once the page scrolls it pins INTO
   that band: the Save / Print / Load buttons, the language switch, the depth
   switch and every section chip stay plainly visible through the translucent
   navbar while every tap lands on `.nav-container`. Nothing looks broken. That
   is exactly why the grocery-list version of this shipped.

   71px / 64px are MEASURED, not chosen: `.nav-container` is `padding: 1.1rem
   2rem`, dropping to `1rem 1.25rem` at 480px where styles.css:1142 also takes
   :root to 15px. Re-measure with a real hit-test (`elementFromPoint` at the
   centre of a header control must resolve to that control, never to
   `.nav-container`) if either changes.
   ========================================================================== */

.in-app header.sticky,
.in-app .sticky {
    top: var(--in-nav-h);
}

@media (max-width: 480px) {
    .in-app { --in-nav-h: 64px; }
}

/* ==========================================================================
   5. TOUCH

   The 44px floor is a TOUCH rule. Compact controls are correct under a mouse,
   and growing them everywhere would cost this form's already-tall header more
   height on exactly the screens with least of it. Sizes are px, never rem:
   styles.css:1142 drops :root to 15px at 480px, so a rem-based 44px target
   renders at 41px on the phones that need it most.
   ========================================================================== */

@media (pointer: coarse), (max-width: 1024px) {
    .in-app .segmented button {
        min-height: 44px;
        padding-left: 0.9rem;
        padding-right: 0.9rem;
    }
    .in-app .repeater-remove {
        width: 44px;
        height: 44px;
    }
    /* Moves WITH the button above. One measurement, two rules. */
    .in-app .repeater-row {
        padding-right: 3.5rem;
    }

    /* The section chips are tap targets, not decoration -- a mis-tap jumps the
       reader to the wrong section of a 27-section form. Upstream sizes them at
       32px, which is fine under a mouse and is what the narrow sweep found.
       COSTED, not assumed: the sticky header measures 229px at 375px and this
       takes it to 241px, because the strip's height follows the chip. 12px of
       every scrolled screen, for the whole session, is the price of the rule --
       and the rule is the site's, so the price is the one to pay. If the header
       ever has to give height back, take it from somewhere that is not a
       target. */
    .in-app .nav-chip {
        min-height: 44px;
    }
}

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

/* ==========================================================================
   6. PRINT

   The printed form is a FIRST-CLASS DELIVERABLE here, not a courtesy: the tool
   exists so a client can either fill it in on screen or print it and write on
   it. Everything below is the upstream print stylesheet, re-pointed at the
   paper palette.

   Two deliberate deviations from upstream:

   1. NO `@page { size: A4 }`. Upstream forces A4; this site's clients are in
      Ottawa, where the printer holds Letter. Forcing a sheet size the tray does
      not have makes the driver scale or clip. The layout is fluid, so letting
      the user's paper win costs nothing. The margin is kept.

   2. `.grid` IS RESTORED, doubled. `tool-base.css` flattens every `.grid` to a
      column flex box in print, because Gecko does not fragment grid containers
      across pages and the other tools each wrap their whole document in one.
      That reasoning does not transfer: every `.grid` here is a small field
      group INSIDE a `.section`, and `.section` already carries
      `break-inside: avoid`, so no grid container ever needs to fragment. Left
      flattened, every side-by-side field pair on the paper form stacks and the
      document runs pages longer for no reason.

      `.in-app.in-app .grid` is (0,3,0), the same as `.tool-app.tool-app .grid`,
      and this file loads later — so it wins on order. That is the documented
      doubled-class contract in tool-base.css. It is the ONLY selector doubled
      here: doubling a tool's print rules wholesale is what flipped the
      planner's headline colour, and the note there says so.
   ========================================================================== */

@media print {

    @page { margin: 0.6in; }

    /* One remap, and every rule above follows it onto paper. */
    .in-app {
        --in-ink: #111;
        --in-dim: #333;
        --in-muted: #555;
        --in-card: #fff;
        --in-inset: #fff;
        --in-page: #fff;
        --in-overlay: #fff;
        --in-line: #bbb;
        --in-line-strong: #888;
        /* The site's amber darkened for paper — the SAME value every other tool
           prints. Two tools on one site handing a client documents in two
           different brand colours is the defect that figure fixed. */
        --in-accent: #8a6420;
        --in-accent-hi: #6d4e19;
        --in-accent-lo: #6d4e19;
        --in-accent-tint: #f7f1e4;
        --in-accent-tint2: #ecdcbf;
        --in-on-accent: #fff;
        --in-error: #111;
        --in-error-tint: transparent;
        --in-nav-h: 0px;
        --in-head-h: 0px;
    }

    /* See deviation 2 in the block comment. The only doubled selector here. */
    .in-app.in-app .grid {
        display: grid;
        align-items: initial;
    }

    /* Identifies the printout: agency, client business, date. */
    .in-app .print-stamp {
        display: block;
        margin-bottom: 0.6rem;
        padding-bottom: 0.3rem;
        border-bottom: 1px solid #bbb;
        font-size: 8pt;
        color: #555;
        letter-spacing: 0.04em;
    }

    /* A SECTION MAY SPLIT ACROSS A PAGE; THE THINGS INSIDE IT MAY NOT.
       This is the one place the port deliberately overrides upstream's print
       stylesheet, and it is the site's own logged lesson: `break-inside: avoid`
       on a CARD sends any card too tall for the remaining space to a fresh
       sheet, so the estimator once printed four pages with two of them half
       empty. On a 27-section form that is 27 chances to waste most of a page,
       and measured on the blank form it did exactly that -- 21 pages with TEN
       of them under 4% ink coverage. Letting sections split and protecting the
       atoms instead gives 17 pages with two, and raises mean coverage from
       4.2% to 5.2%. Four sheets of paper, on a document whose whole point is
       that clients print it.

       Re-measure with ink coverage, not page count alone: a page count can
       fall while the document gets worse, and what makes this right is that
       no label was separated from the line it labels. */
    .in-app .section {
        background: #fff !important;
        box-shadow: none !important;
        border: none !important;
        border-top: 2px solid #111 !important;
        border-radius: 0 !important;
        padding: 0.5rem 0 1.25rem !important;
        margin-bottom: 0.5rem !important;
        scroll-margin-top: 0;
        break-inside: auto;
        page-break-inside: auto;
    }

    /* The atoms. A field group is label + control + help text, and splitting
       one puts a question on one sheet and its writing line on the next. */
    .in-app .section .grid > *,
    .in-app .section label {
        break-inside: avoid;
        page-break-inside: avoid;
    }

    .in-app h2.section-title {
        font-size: 14pt;
        margin-bottom: 0.5rem;
        /* A heading alone at the foot of a page is a promise the sheet does not
           keep. Now that sections split, this is reachable. */
        break-after: avoid;
        page-break-after: avoid;
    }

    .in-app .section-num {
        background: var(--in-accent) !important;
        color: #fff !important;
        print-color-adjust: exact;
        -webkit-print-color-adjust: exact;
    }

    /* A one-line answer prints as a RULE to write on, not as a boxed control.
       A box the reader is meant to write inside has to be as tall as
       handwriting; a rule does not, so this is what keeps the paper form to a
       sane page count. */
    .in-app .field-input,
    .in-app .field-select {
        border: none !important;
        border-bottom: 1px solid #555 !important;
        border-radius: 0 !important;
        padding: 0.15rem 0 !important;
        background: transparent !important;
        box-shadow: none !important;
        min-height: 1.4rem !important;
        font-size: inherit !important;
    }

    /* A <select> on paper is a control nobody can operate: it shows the current
       value and hides every option, so on a blank form the question cannot be
       answered at all. It cannot be made to work -- what it CAN stop doing is
       looking like a working control. Dropping the native appearance leaves a
       writing rule like every other one-line field, so the reader writes the
       answer in. Found by reading the printed page, not the CSS: the rule
       above already made it a rule, and the engine drew the arrow on top. */
    .in-app .field-select {
        -webkit-appearance: none;
        -moz-appearance: none;
        appearance: none;
        background-image: none !important;
    }

    .in-app .field-textarea {
        border: 1px solid #888 !important;
        border-radius: 0 !important;
        background: transparent !important;
        box-shadow: none !important;
        min-height: 3.5rem !important;
        font-size: inherit !important;
    }

    /* A textarea placeholder is taller than the printed box and gets sliced in
       half. The help text below carries the same guidance, so drop it. */
    .in-app .field-textarea::placeholder { color: transparent !important; }

    .in-app .field-label {
        font-weight: 700;
        margin-top: 0.4rem;
    }

    .in-app .field-error { display: none; }

    /* On screen this is a hint. On paper it is the ONLY instruction the person
       filling it in gets, so it has to survive printing. */
    .in-app .field-help {
        display: block;
        font-size: 8pt;
        color: #555;
        margin-top: 0.15rem;
    }

    /* A thin border alone is nearly invisible on paper — force the native
       control to print so a ticked box actually reads as ticked. */
    .in-app .pick-tile,
    .in-app .palette-tile {
        border: 1px solid #999 !important;
        background: #fff !important;
        min-height: 0 !important;
        box-shadow: none !important;
        break-inside: avoid;
        page-break-inside: avoid;
    }

    .in-app .pick-tile { padding: 0.25rem 0.45rem !important; }

    .in-app .pick-tile input,
    .in-app .palette-tile input {
        accent-color: #111 !important;
        opacity: 1 !important;
        print-color-adjust: exact;
        -webkit-print-color-adjust: exact;
    }

    /* Ticked reads as a heavier border, never as a fill — the same weight-not-
       hue rule the nav dots follow, and the only one that works in greyscale. */
    .in-app .pick-tile:has(input:checked) {
        background: #fff !important;
        border-color: #111 !important;
        border-width: 2px !important;
    }
    .in-app .palette-tile:has(input:checked) {
        border-color: #111 !important;
        border-width: 2px !important;
        box-shadow: none !important;
    }

    .in-app .repeater-row {
        background: #fff !important;
        border: 1px dashed #888 !important;
        padding-right: 1rem !important;
        margin-bottom: 0.45rem;
        break-inside: avoid;
        page-break-inside: avoid;
    }

    .in-app .repeater-remove,
    .in-app .repeater-add,
    .in-app .toast,
    .in-app .review-dialog,
    .in-app .skip-link {
        display: none !important;
    }

    /* The `.sticky` rule in Layer 4 would otherwise pin the header on paper. */
    .in-app .sticky {
        position: static !important;
        top: auto !important;
    }

    /* A printed mailto is text — the URL adds nothing a reader can act on. */
    .in-app a[href^="mailto:"] {
        text-decoration: none;
        color: #111;
    }
}
