/* Shared shell for every guided Unfussed job page. The listing door keeps its own
   bespoke page; these four are rendered from the play manifest, so they share one
   stylesheet rather than four near-copies. */
/* Every token that differs between the two themes is written twice on purpose,
   and the pair is the whole theming mechanism — see the theme section at the
   foot of this file for why it is light-dark() rather than a media query, and
   why the plain value is repeated above each pair. */
:root {
  color-scheme: light dark;
  --ink: #162019;
  --muted: #667064;
  --muted: light-dark(#667064, #a6b0a3);
  --paper: #f7f3ea;
  --paper: light-dark(#f7f3ea, #12180f);
  --card: #fffdf8;
  --card: light-dark(#fffdf8, #1b2717);
  --line: #d8d5c9;
  --line: light-dark(#d8d5c9, rgba(243, 240, 230, .16));
  --acid: #d9ff57;
  --green: #163d29;
  /* --green is a dark ink. It is right behind lime and right for a filled
     panel, and it disappears on the unlit ground — so everything that sets
     TYPE in the brand green reads --green-ink, and everything that fills a
     shape with it keeps --green. */
  --green-ink: #163d29;
  --green-ink: light-dark(#163d29, #8fe0af);
  --orange: #ff7657;
  /* Also type rather than fill — the "+" on a question, a step number, the
     label on a before/after panel — so it lifts on the unlit ground too. */
  --orange-ink: #b03d20;
  --orange-ink: light-dark(#b03d20, #ffab8f);
  --shadow: 0 24px 70px rgba(32, 38, 28, .12);
  --shadow: 0 24px 70px light-dark(rgba(32, 38, 28, .12), rgba(0, 0, 0, .5));
  --on-lime: var(--ink);
  --text: var(--ink);
  --text: light-dark(var(--ink), #f3f0e6);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background:
    radial-gradient(circle at 12% 8%, rgba(217, 255, 87, .3), transparent 24rem),
    radial-gradient(circle at 90% 28%, rgba(255, 118, 87, .14), transparent 26rem),
    var(--paper);
  /* The two washes are laid over the paper at a third of their strength on the
     unlit theme; at full strength they read as smears rather than light. */
  background:
    radial-gradient(circle at 12% 8%, light-dark(rgba(217, 255, 87, .3), rgba(217, 255, 87, .1)), transparent 24rem),
    radial-gradient(circle at 90% 28%, light-dark(rgba(255, 118, 87, .14), rgba(255, 118, 87, .08)), transparent 26rem),
    var(--paper);
  color: var(--text);
  font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }
button, input, textarea, select { font: inherit; }
button { cursor: pointer; }
/* --text rather than --ink: the two are the same colour on paper, and only
   --text follows the theme, so this is one declaration instead of two. */
:focus-visible { outline: 3px solid var(--text); outline-offset: 2px; border-radius: 4px; }
.shell { width: min(1180px, calc(100% - 40px)); margin: 0 auto; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

.topbar { display: flex; align-items: center; justify-content: space-between; min-height: 84px; gap: 24px; }
.brand { display: inline-flex; align-items: center; gap: 11px; text-decoration: none; font-weight: 850; letter-spacing: -.03em; }
.brand-mark {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 2px solid var(--ink);
  border-radius: 14px;
  background: var(--acid);
  box-shadow: 3px 3px 0 var(--ink);
  transform: rotate(-3deg);
}
/* The full knot needs 32px to resolve its weave; below that the sibling mark
   is used instead. See design/logo/RULES.md. */
.brand-mark img { display: block; width: 32px; height: 32px; }
.nav { display: flex; align-items: center; gap: 20px; color: var(--muted); font-size: 14px; font-weight: 700; }
.nav a { text-decoration: none; }
.nav a:hover { color: var(--text); }
.credit-pill { border: 1px solid var(--line); border-radius: 999px; padding: 9px 13px; background: rgba(255, 253, 248, .65); color: var(--ink); }

/* The way back in for somebody who has already paid.
   It carries .credit-pill so that the narrow-screen rule below — which hides
   every nav link that is not a pill — keeps it, because a returning buyer is
   likelier to be on a phone than anyone else on this page. It is deliberately
   the quieter of the two pills: no fill, muted text. On a job page it sits
   beside the one that takes the money, and a second filled pill there would
   read as a competing offer rather than as a door back to what is already
   bought. Once a balance is known it earns full-strength text, because at that
   point it is telling the buyer something worth money.

   Selected as `.nav .account-pill` rather than `.account-pill`, which is not
   tidiness: eight of the nineteen job pages still carry a page-local copy of
   the `.credit-pill` rule in an inline <style> block, and an inline block wins
   a specificity tie against a linked stylesheet by source order alone. A
   single-class selector here would therefore be overridden on exactly those
   eight pages and nowhere else — a filled account pill on eight pages and an
   outlined one on eleven, from a difference nothing in either file mentions. */
.nav .account-pill { background: transparent; color: var(--muted); }
.nav .account-pill:hover, .nav .account-pill:focus-visible { color: var(--text); border-color: var(--text); }
.nav .account-pill.has-points { color: var(--text); border-color: var(--text); }
.account-short { display: none; }

.eyebrow { display: inline-flex; align-items: center; gap: 9px; margin-bottom: 20px; color: var(--green-ink); font-size: 13px; font-weight: 850; letter-spacing: .08em; text-transform: uppercase; }
.eyebrow::before { content: ""; width: 25px; height: 3px; border-radius: 99px; background: var(--orange); }

h1 { max-width: 720px; margin: 0; font-size: clamp(46px, 6.2vw, 78px); line-height: .93; letter-spacing: -.07em; font-weight: 900; }
h1 .marker { position: relative; z-index: 0; white-space: nowrap; }
h1 .marker::after { content: ""; position: absolute; left: -.02em; right: -.06em; bottom: .03em; height: .24em; z-index: -1; background: var(--acid); transform: rotate(-1deg); }
.lead { max-width: 620px; margin: 24px 0 0; color: #505b50; font-size: clamp(17px, 1.8vw, 21px); line-height: 1.5; letter-spacing: -.02em; }

.intake-layout { display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(340px, .98fr); gap: clamp(40px, 6vw, 84px); padding: 60px 0 90px; align-items: start; }

.job-form { margin-top: 30px; padding: 18px; display: grid; gap: 16px; border: 2px solid var(--ink); border-radius: 18px; background: var(--card); box-shadow: 7px 7px 0 var(--ink); }
.field { display: grid; gap: 6px; }
.field label { font-size: 14px; font-weight: 800; letter-spacing: -.01em; }
.field .optional { color: var(--muted); font-weight: 650; }
.field input, .field textarea { width: 100%; padding: 12px 14px; border: 1px solid var(--line); border-radius: 12px; outline: 0; background: #fffefb; color: var(--text); font-size: 16px; line-height: 1.5; resize: vertical; }
.field input:focus, .field textarea:focus { border-color: var(--ink); box-shadow: 0 0 0 3px rgba(217, 255, 87, .75); }
.input-tabs { display: flex; gap: 6px; }
.input-tab { flex: 1; min-height: 42px; border: 1px solid var(--line); border-radius: 11px; background: transparent; color: var(--muted); font-size: 14px; font-weight: 800; }
.input-tab.is-active { border-color: var(--ink); background: var(--acid); color: var(--ink); }
.input-panel .field { margin: 0; }
.consent-row { display: flex; align-items: flex-start; gap: 12px; min-height: 44px; padding: 6px 4px; border-radius: 10px; cursor: pointer; font-size: 16px; line-height: 1.5; }
.consent-row:hover { background: rgba(217, 255, 87, .18); }
.consent-row input { flex: 0 0 auto; width: 22px; height: 22px; margin: 2px 0 0; accent-color: var(--ink); }
.consent-row span { font-weight: 600; }
.field input::placeholder, .field textarea::placeholder { color: #899087; }
.field .counter { justify-self: end; color: var(--muted); font-size: 12px; font-weight: 700; }
.field .counter.short { color: #a22616; }
/* The character count and the .txt loader share one row under the textarea. */
.paste-tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
.paste-tools .counter { justify-self: auto; }
.file-pick { cursor: pointer; text-decoration: underline; color: var(--muted); font-size: 12px; font-weight: 700; }
.file-pick input { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.panel-note { margin: 9px 2px 0; color: var(--muted); font-size: 12px; font-weight: 650; line-height: 1.5; }

/* The upload intake. Only the photo jobs draw it, and they draw it first,
   because on those jobs the photographs are the job and the typed fields only
   describe them. */
.photo-field input[type="file"] { width: 100%; padding: 14px; border: 1px dashed var(--line); border-radius: 12px; background: #fffefb; color: var(--muted); font-size: 14px; }
.photo-field input[type="file"]::file-selector-button { margin-right: 12px; min-height: 38px; padding: 0 14px; border: 1px solid var(--ink); border-radius: 9px; background: var(--acid); color: var(--ink); font-size: 13px; font-weight: 800; cursor: pointer; }
.photo-field .panel-note { margin-top: 2px; }
.photo-list { display: grid; gap: 6px; margin: 4px 0 0; padding: 0; list-style: none; }
.photo-list li { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border: 1px solid var(--line); border-radius: 11px; background: rgba(255, 253, 248, .72); font-size: 14px; }
.photo-list .photo-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; }
.photo-list .photo-size { flex: 0 0 auto; color: var(--muted); font-size: 12px; font-weight: 700; }
.photo-list .photo-remove { flex: 0 0 auto; min-height: 36px; padding: 0 11px; border: 1px solid var(--line); border-radius: 9px; background: transparent; color: var(--muted); font-size: 12px; font-weight: 800; }
.photo-list .photo-remove:hover { border-color: var(--ink); color: var(--text); }

/* A job whose provider key this deployment does not hold. Distinct from
   :disabled's wait cursor, which means "in flight" — this one is not coming
   back on this click, so it must not look like something that is loading. */
.primary-button.is-unavailable:disabled { cursor: not-allowed; }
.unavailable { display: none; margin: 18px auto 0; padding: 14px 18px; border: 1px solid #ddbf75; border-radius: 12px; background: #fff5cf; color: #674c09; font-size: 14px; font-weight: 700; line-height: 1.55; }
.unavailable.show { display: block; }

.primary-button { min-height: 56px; padding: 0 22px; border: 0; border-radius: 12px; background: var(--ink); color: #fff; font-weight: 850; letter-spacing: -.015em; transition: transform .18s ease, background .18s ease; }
.primary-button:hover { transform: translateY(-2px); background: var(--green); }
.primary-button:disabled { cursor: wait; opacity: .65; transform: none; }
.form-error { min-height: 22px; margin: 12px 0 0; color: #a22616; font-size: 14px; font-weight: 750; }
/* T-LU-287 — the mount point for Stripe's own Embedded Checkout iframe,
   inserted after #jobForm when it is hidden for a live payment. No
   border/background of its own competing with Stripe's own chrome inside
   the iframe; min-height matches the form it replaces so the page does not
   collapse while Stripe's script is still loading the form. */
.embedded-checkout { min-height: 420px; margin-top: 18px; }
.form-note { display: flex; flex-wrap: wrap; gap: 7px 18px; margin-top: 16px; color: var(--muted); font-size: 13px; font-weight: 650; }
/* Direct children only — the cost note carries a nested span, and a descendant
   selector put a second tick in the middle of the sentence. */
.form-note > span::before { content: "✓"; margin-right: 7px; color: var(--green-ink); font-weight: 900; }

/* Reused, not reinvented: the same class the free SEO checker pages style in
   seo-scenario.css, so the "not legal advice" and draft-review asides read as
   one component wherever they appear — before the buy button here, and again
   under the delivered result (#deliveryNotice, appended by renderResult()). */
.legal-note { margin-top: 16px; padding: 14px 16px; border: 1px solid var(--line); border-radius: 14px; background: rgba(255, 253, 248, .5); color: var(--muted); font-size: 13px; line-height: 1.5; }
#deliveryNotice { margin: 28px 0 0; }

.side-card { padding: 26px; border: 1px solid var(--line); border-radius: 22px; background: rgba(255, 253, 248, .72); box-shadow: var(--shadow); }
.side-card h2 { margin: 0 0 14px; font-size: 20px; letter-spacing: -.03em; }
.side-card ul { margin: 0; padding-left: 20px; color: var(--muted); }
.side-card li { margin: 9px 0; line-height: 1.5; font-size: 14px; }
.price-lock { display: flex; align-items: baseline; gap: 10px; margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--line); }
.price-lock strong { font-size: 34px; letter-spacing: -.06em; }
.price-lock span { color: var(--muted); font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; }

/* The job hero image — one overhead cut-paper still life per job, named after the
   page's own slug. See design/imagery/STYLE.md.

   It is deliberately not at the top of the page. These are buy pages, and 16:9 of
   decoration above the intake buys nothing and costs a fold. Both placements below
   put it beside or after the form, never in front of it.

   The width and height attributes on the img are the real 1600x900, so the box is
   reserved before the bytes arrive; a listing that reflows under the cursor on a
   paid page is worth more than the picture. */
.job-hero { display: block; position: relative; border-radius: 16px; }
.job-hero img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: inherit;
}
/* The inner keyline, drawn ON the image rather than around it.
   An inset box-shadow will not do this job: on a replaced element the content
   paints over the inset shadow, so the rule applies and nothing appears. Hence a
   pseudo-element on the <picture>, which is already the wrapper. */
.job-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid rgba(22, 32, 25, .12);
  border-radius: inherit;
  pointer-events: none;
}

/* Placement one: the head of the "what you get" rail. At >900px that rail is the
   second grid column, so the image is above the fold without moving the form down
   by a single pixel; below 900px the rail stacks after the form and the image
   lands below it. */
.side-card .job-hero { margin: -2px 0 20px; }

/* Placement two: the argued pages, whose hero column already carries a worked
   example of the report. Two competing pictures side by side is one too many, so
   there the still life opens the deliverables instead, set to the same measure as
   the lead above it. */
.section-lead + .job-hero { max-width: 620px; margin: 36px 0 0; }

.notice { display: none; margin: 18px auto 0; padding: 13px 17px; border: 1px solid #ddbf75; border-radius: 12px; background: #fff5cf; color: #674c09; font-size: 14px; font-weight: 700; }
.notice.show { display: block; }
.test-banner { display: none; margin: 18px auto 0; padding: 14px 18px; border: 3px dashed #b4400f; border-radius: 14px; background: #fff2ec; color: #b4400f; font-size: 14px; font-weight: 800; line-height: 1.5; }
.test-banner.show { display: block; }

.result-view { display: none; padding: 54px 0 100px; }
body.result-mode .intake { display: none; }
body.result-mode .result-view { display: block; }
.result-kicker { color: var(--green-ink); font-size: 12px; font-weight: 900; letter-spacing: .1em; text-transform: uppercase; }
.result-title { max-width: 850px; margin: 14px 0 10px; font-size: clamp(38px, 5.4vw, 66px); line-height: .99; letter-spacing: -.06em; }

.loading-card { max-width: 700px; margin-top: 40px; padding: 28px; border: 2px solid var(--ink); border-radius: 22px; background: var(--card); box-shadow: 7px 7px 0 var(--ink); }
.loading-card h2 { margin: 22px 0 7px; font-size: 21px; }
.loading-card p { margin: 0; color: var(--muted); line-height: 1.55; }
.result-error { display: none; max-width: 700px; margin-top: 36px; padding: 22px; border: 1px solid #d48a79; border-radius: 18px; background: #fff1ed; color: #842d1d; font-weight: 700; }

.result-panel { display: none; margin-top: 38px; border: 1px solid var(--line); border-radius: 22px; background: var(--card); overflow: hidden; }
.result-section { padding: 28px; }
.result-section + .result-section { border-top: 1px solid var(--line); }
.result-section h2 { margin: 0 0 16px; font-size: 22px; letter-spacing: -.03em; }
.copy-box { padding: 20px; border-radius: 15px; background: #f1eee4; }
.copy-box p { margin: 0; white-space: pre-wrap; line-height: 1.6; }
.copy-button { margin-top: 12px; min-height: 42px; padding: 0 14px; border: 1px solid var(--ink); border-radius: 10px; background: var(--card); font-size: 13px; font-weight: 800; }
.plain-text { margin: 0; white-space: pre-wrap; line-height: 1.6; color: #3d463d; }
.item-list { margin: 0; padding-left: 22px; }
.item-list li { margin: 9px 0; line-height: 1.5; }
/* Questions put to a named third party, not actions for the buyer's own
   document — see the "questions" branch of renderBlock. Numbered, because the
   buyer works down them with somebody on the phone. */
.question-list { margin: 0; padding-left: 24px; }
.question-list li { margin: 11px 0; line-height: 1.55; }
.question-list li::marker { font-weight: 800; color: var(--muted); }
.clip { margin: 0 0 18px; }
.clip:last-of-type { margin-bottom: 0; }
.clip video { display: block; width: 100%; max-width: 720px; border: 1px solid var(--line); border-radius: 15px; background: #0f150c; }
.clip figcaption { margin-top: 8px; color: var(--muted); font-size: 13px; font-weight: 700; }
.record { padding: 16px 0; border-top: 1px solid var(--line); }
.record:first-of-type { border-top: 0; padding-top: 0; }
.record h3 { margin: 0 0 6px; font-size: 16px; }
.record p { margin: 0 0 6px; color: var(--muted); line-height: 1.5; }
.record .record-extra { color: var(--green-ink); font-size: 13px; font-weight: 750; }
/* A two-quote comparison row. Both cells carry the SAME class, the same colour
   and the same weight — see the "comparison" branch of renderBlock. Only the
   line naming what differs is set apart, because that one is about the pair
   rather than about either quote. */
.record .cell { display: flex; gap: 10px; margin: 0 0 6px; color: var(--muted); line-height: 1.5; }
.record .cell-label { flex: 0 0 66px; font-weight: 800; color: var(--text); }
.record .cell-difference { margin: 8px 0 0; color: var(--green-ink); font-size: 13px; font-weight: 750; }

/* --- the thread ----------------------------------------------------------

   After paying, the buyer is in a conversation about one job rather than on a
   page that gets replaced. #resultPanel keeps its id and its display toggle and
   becomes the thread: its own card border and ground come off, because every
   turn is now a card and a card inside a card reads as one long document again.

   Colour comes from the category tokens in /categories.css. Every accent below
   reads one variable, and that variable is var(--cat, …) — so a job page that
   grows a data-cat attribute is already the right colour in both themes with no
   rule added here. The fallback is the brand green the result kicker uses, and
   it has to be resolved per theme rather than once — which --green-ink already
   does, so this is one declaration and not a second one in a dark block. */
.result-panel.thread, .thread-foot { --accent: var(--cat, var(--green-ink)); }

/* The composer hides half of itself when the thread can go no further, and it
   does that with the `hidden` attribute — which is only a UA rule of
   `display: none` and therefore loses to any class here that sets a display.
   Without this line the price of a turn that cannot be sent stays on screen
   beside a dead button, under a sentence saying the job is finished. Caught in a
   capture, not in review: every element involved was correctly marked hidden. */
.thread-foot [hidden] { display: none; }
.result-panel.thread { border: 0; border-radius: 0; background: none; overflow: visible; }

.turn { margin-top: 16px; border: 1px solid var(--line); border-radius: 18px; background: var(--card); overflow: hidden; }
.turn:first-child { margin-top: 0; }
/* Focus lands here after a send, so the ring has to fit a card rather than the
   4px the sitewide rule assumes for a control. */
.turn:focus-visible { outline-offset: 3px; border-radius: 18px; }
.turn-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 13px 22px; border-bottom: 1px solid var(--line); }
.turn-who { margin: 0; color: var(--muted); font-size: 12px; font-weight: 900; letter-spacing: .1em; text-transform: uppercase; }

/* What the generation took. Tabular figures because a thread is a column of
   them and 387 above 1,204 should line up on the digit, not on the glyph. */
.turn-cost {
  margin: 0;
  padding: 4px 12px;
  border: 1.5px solid color-mix(in srgb, var(--accent) 32%, transparent);
  border-radius: 999px;
  background: var(--cat-wash, color-mix(in srgb, var(--accent) 10%, var(--card)));
  color: var(--accent);
  font-size: 13px;
  font-weight: 850;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Which version this turn is, beside what it cost. Quieter than the badge next
   to it and set in the same tabular figures: "Version 2 of 3" and "392 points"
   are read as one line, and a heading that outweighed the charge would be the
   page emphasising the wrong half of it. */
.turn-version {
  margin: 0;
  color: var(--muted);
  font-size: 12px;
  font-weight: 850;
  letter-spacing: .06em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

/* An earlier version, folded to its heading and opening in place. The
   disclosure is a <details>, so it already works with the keyboard and on the
   first paint; what these rules add is a summary that looks like the control it
   is, and a folded block that reads as quieter than the version above it.

   No border-top: a delivery turn's body sits directly under a header that
   already draws one, and a second line there is a 2px rule nobody asked for. */
.turn-earlier { background: color-mix(in srgb, var(--accent) 4%, transparent); }
.turn-earlier > summary {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 44px;
  padding: 11px 22px;
  color: var(--accent);
  font-size: 14px;
  font-weight: 800;
  cursor: pointer;
  list-style: none;
}
.turn-earlier > summary::-webkit-details-marker { display: none; }
.turn-earlier > summary::before { content: "▸"; font-size: 11px; transition: transform .15s ease; }
.turn-earlier[open] > summary::before { transform: rotate(90deg); }
.turn-earlier[open] > summary { border-bottom: 1px solid var(--line); }
.turn-earlier > summary:hover { background: color-mix(in srgb, var(--accent) 9%, transparent); }

/* The buyer's own turns — what they sent, each change they asked for, and every
   answer they gave on the way in. Tinted and ruled on the left so the two
   voices are told apart by more than the word at the top of the card. */
.turn-brief, .turn-ask, .turn-answer {
  border-left: 4px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--card));
}
.turn-brief .turn-body, .turn-ask .turn-body, .turn-answer .turn-body { display: grid; gap: 15px; padding: 20px 22px; }
.brief-item { display: grid; gap: 4px; }
.brief-label { color: var(--muted); font-size: 12px; font-weight: 800; letter-spacing: -.01em; }
.brief-value { margin: 0; white-space: pre-wrap; line-height: 1.6; font-size: 15px; }

.turn-pending .turn-body { padding: 22px; }
.turn-pending .plain-text { margin: 0; }

/* The running total, directly under the last turn and above the composer: what
   this thread has cost so far, and what is left on the pack. */
.thread-total { margin: 20px 0 0; padding: 0 4px; color: var(--text); font-size: 14px; font-weight: 750; line-height: 1.6; }

/* The composer. It carries the same weight as the intake form it replaces on
   this screen, because it does the same thing — it starts a job — and it is now
   the only control on the page that can spend points. */
.composer { margin-top: 18px; padding: 20px 22px; border: 2px solid var(--ink); border-radius: 18px; background: var(--card); box-shadow: 5px 5px 0 var(--ink); }
.composer h2 { margin: 0 0 12px; font-size: 19px; letter-spacing: -.03em; }
.composer textarea { width: 100%; padding: 12px 14px; border: 1px solid var(--line); border-radius: 12px; outline: 0; background: #fffefb; color: var(--text); font-size: 16px; line-height: 1.5; resize: vertical; }
.composer textarea:focus { border-color: var(--ink); box-shadow: 0 0 0 3px rgba(217, 255, 87, .75); }
.composer textarea::placeholder { color: #899087; }
.composer textarea:disabled { opacity: .6; }

/* The price and the button that spends it, in one row and never further apart
   than this. The row wraps on a narrow screen, which puts the price directly
   above the button rather than beside it — still the last thing read before it
   is pressed. */
.composer-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; margin-top: 12px; }
.composer-cost { flex: 1 1 240px; margin: 0; color: var(--text); font-size: 14px; font-weight: 750; line-height: 1.5; font-variant-numeric: tabular-nums; }
.composer-send { flex: 0 0 auto; min-height: 48px; padding: 0 20px; border: 1px solid transparent; border-radius: 12px; font-size: 15px; font-weight: 850; letter-spacing: -.015em; transition: transform .18s ease, background .18s ease; }
.composer-send:not(:disabled) { background: var(--ink); color: #fff; }
.composer-send:hover:not(:disabled) { transform: translateY(-2px); background: var(--green); }
/* A send that cannot be afforded loses its fill rather than dimming it. Dimming
   is what the rest of this sheet does for "in flight", and on the unlit theme a
   55%-opacity acid button is still the brightest thing on the card — it read as
   the thing to press in a dark capture, which is the exact opposite of what a
   blocked charge should look like. Outlined and muted reads as unavailable in
   both themes, and it is the only disabled state on this page that means "this
   will not happen" rather than "wait". */
.composer-send:disabled { cursor: not-allowed; transform: none; border-color: var(--line); background: transparent; color: var(--muted); }
.composer-topup { display: inline-block; margin-top: 12px; min-height: 44px; padding: 12px 16px; border: 1px solid var(--ink); border-radius: 11px; font-size: 14px; font-weight: 800; text-decoration: none; }
.composer-topup:hover { background: var(--acid); }
.composer-error { min-height: 0; margin-top: 10px; }
.composer-error:empty { display: none; }
.composer-closed { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.6; }
/* Nothing left to send: it keeps its place in the column and gives up the
   weight of a control, because an inked card with a drop shadow reads as
   something to press however quiet the sentence inside it is. */
.composer.is-closed { border: 1px solid var(--line); box-shadow: none; }

/* --- the intake as a thread ------------------------------------------------

   The same turns, before the money rather than after it: one question on
   screen, the answer to it left underneath as a turn of the buyer's own, then
   the next question. So the page a buyer fills in and the page they come back
   to are recognisably one conversation about one job.

   NOTHING HERE DRAWS A WAIT, and that is a rule rather than an omission. The
   questions ship in config/plays.json and arrive with /api/config, so a press
   reveals the next one in the same frame — there is no model call, no points
   and no pence between two turns. A spinner, a fade or a transition long enough
   to read as consideration would be this stylesheet inventing a deliberation
   the product is not doing, which the DMCC Act 2024 has a name for. The honest
   version is also the better interface: the count in each header is simply
   true. */
#jobFields { --accent: var(--cat, var(--green-ink)); }
#jobFields .turn { margin-top: 14px; }

/* One question at a time is drawn by hiding the rest with the `hidden`
   attribute — which is only a UA rule of `display: none` and therefore loses to
   ANY class below that sets a display. .step-controls, .step-actions and
   .step-choices all set one. Without this line every answered question keeps
   its box and its Next button on screen, and the interview is nine questions at
   once again with the answers interleaved.
   Caught in a capture, not in review, and not for the first time: the composer
   has the identical line at .thread-foot [hidden] for the identical reason,
   which is why this is written down rather than left as one selector. */
#jobFields [hidden] { display: none; }

/* The product's first turn: what this job is and what it can cost, before it
   asks for anything. Tinted rather than boxed, because it is a statement and
   not something to fill in. */
.turn-opening { border-color: transparent; background: color-mix(in srgb, var(--accent) 9%, var(--card)); }
.turn-opening .turn-body { display: grid; gap: 9px; padding: 18px 22px; }
.opening-price { margin: 0; color: var(--accent); font-size: 15px; font-weight: 850; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.opening-ask { margin: 0; font-size: 15px; line-height: 1.55; }

/* A question. It carries the manifest's own label as a heading rather than as a
   field label, because at one question per turn the question IS the card. */
.turn-question .turn-body { display: grid; gap: 14px; padding: 18px 22px; }
.step-question { margin: 0; font-size: 17px; font-weight: 850; line-height: 1.35; letter-spacing: -.03em; }
.step-question .optional { font-size: 15px; font-weight: 650; letter-spacing: -.01em; color: var(--muted); }
/* Which question this is, out of how many the manifest holds. Both figures are
   counted; neither is estimated, and there is nothing else in this header. */
.turn-step { margin: 0; color: var(--muted); font-size: 12px; font-weight: 800; letter-spacing: .06em; font-variant-numeric: tabular-nums; }
.step-controls { display: grid; gap: 14px; }
.step-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }

.step-next {
  min-height: 44px;
  padding: 0 22px;
  border: 1px solid transparent;
  border-radius: 11px;
  background: var(--ink);
  background: light-dark(var(--ink), var(--acid));
  color: #fff;
  color: light-dark(#fff, var(--ink));
  font-size: 15px;
  font-weight: 850;
  letter-spacing: -.015em;
}
/* Lit, the fill goes from ink to the brand green the primary button already
   goes to. Unlit, the fill is acid and the same move would be invisible, so it
   is mixed a little brighter instead — derived from the token rather than typed
   as a second lime, which is the rule the rest of this section keeps. */
.step-next:hover { background: var(--green); background: light-dark(var(--green), color-mix(in srgb, var(--acid) 86%, #fff)); }
/* Offered only where the manifest marked the field optional. Outlined rather
   than filled: skipping is a real answer, but it is not the one being asked
   for, and two filled buttons side by side is a page with no recommendation. */
.step-skip {
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: transparent;
  color: var(--muted);
  font-size: 14px;
  font-weight: 800;
}
.step-skip:hover { border-color: var(--text); color: var(--text); }

/* Going back. It lives on the buyer's own turn, because that is the thing being
   changed, and it is the quietest control on the card — an interview that
   shouted CHANGE nine times would read as nine things left to do. */
.step-change {
  justify-self: start;
  min-height: 44px;
  padding: 0 12px;
  margin: -6px 0 -6px -12px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--accent);
  font-size: 13px;
  font-weight: 800;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.step-change:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
/* A question the buyer was not obliged to answer, and did not. Said rather than
   left as a gap: a thread with a silent hole in it reads as a bug. */
.answer-skipped { margin: 0; color: var(--muted); font-size: 15px; font-style: italic; }

/* A closed set the server compares exactly. One press is the answer, so these
   are the only controls in the intake with no Next after them. */
.step-choices { display: flex; flex-wrap: wrap; gap: 8px; }
.step-choice {
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--text);
  font-size: 15px;
  font-weight: 750;
}
.step-choice:hover { border-color: var(--text); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.step-choice.is-chosen { border-color: var(--ink); background: var(--acid); color: var(--ink); font-weight: 850; }

/* T-333 — the shared builder (public/ui/question.js) emits `.choices` and
   `.choice` instead of the legacy `.step-choices` / `.step-choice`. Mirror
   the chip look above so the closed-set chips a job page renders through
   buildQuestion pick up the same pill / hover / selected styling. */
.choices { display: flex; flex-wrap: wrap; gap: 8px; }
.choice {
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--text);
  font-size: 15px;
  font-weight: 750;
}
.choice:hover { border-color: var(--text); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.choice.on { border-color: var(--ink); background: var(--acid); color: var(--ink); font-weight: 850; }

/* Consent, held back until the last question is answered and then given a turn
   of its own. Boxed and inked rather than tinted like the product's other
   turns: it is the one card on this page that is an ACT, and the pay button
   directly under it is the only thing that follows. */
.turn-consent { border: 2px solid var(--ink); border-color: var(--ink); border-color: light-dark(var(--ink), rgba(243, 240, 230, .22)); }
.turn-consent .turn-body { padding: 14px 18px; }

/* The keep-this-link line, built by unfussed-job.js after a delivery.
   Deliberately quiet and deliberately outside the thread: this is how to return
   to what was bought, not part of the thing bought. */
.job-ledger { margin-top: 20px; padding: 18px 22px; border: 1px solid var(--line); border-radius: 18px; background: rgba(255, 253, 248, .72); }
.job-ledger p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.6; }

.next-jobs { display: none; margin-top: 34px; padding: 26px; border-radius: 22px; background: var(--green); color: #fff; box-shadow: var(--shadow); }
.next-jobs.show { display: block; }
.next-jobs h2 { margin: 0 0 8px; font-size: 24px; letter-spacing: -.04em; }
.next-jobs p { margin: 0 0 18px; color: #bed0c1; line-height: 1.55; }
.next-jobs ul { display: flex; flex-wrap: wrap; gap: 10px; margin: 0; padding: 0; list-style: none; }
.next-jobs a { display: inline-flex; align-items: center; gap: 8px; padding: 11px 15px; border: 1px solid rgba(255,255,255,.28); border-radius: 999px; background: rgba(255,255,255,.09); text-decoration: none; font-size: 14px; font-weight: 800; }
.next-jobs a:hover { background: var(--acid); color: var(--ink); border-color: var(--acid); }
.next-jobs a[aria-disabled="true"] { opacity: .45; pointer-events: none; }
.next-jobs .cost { font-weight: 650; opacity: .8; }

/* The static cross-links every job page carries, in the marketing view rather
   than the result view: .next-jobs above is built by JavaScript after a delivery
   and is filtered by the balance left, so it is invisible to a crawler and to a
   visitor who has not paid. Until this existed, these pages linked only to
   /fix-my-listing and never to each other, which left three of the four things
   Unfussed sells with no on-page route between them. */
.more-jobs { padding: 44px 0 90px; border-top: 1px solid var(--line); }
.more-jobs h2 { margin: 0 0 8px; font-size: 32px; letter-spacing: -.04em; }
.more-jobs > p { max-width: 620px; margin: 0 0 26px; color: var(--muted); line-height: 1.6; }
.more-jobs ul { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 14px; margin: 0; padding: 0; list-style: none; }
/* Each sibling card carries the colour and glyph of the job it points at, so
   the cross-links read as a map of the catalogue rather than a list of names.
   The colour comes from data-cat via /categories.css — nothing here names a
   category, and a new job inherits this with no CSS change. */
.more-jobs li a { display: grid; align-content: start; gap: 7px; height: 100%; padding: 20px; border: 1px solid var(--line); border-left: 4px solid var(--cat, var(--line)); border-radius: 18px; background: rgba(255, 253, 248, .72); text-decoration: none; transition: transform .18s ease, border-color .18s ease; }
.more-jobs li a:hover { transform: translateY(-2px); border-color: var(--ink); border-left-color: var(--cat, var(--ink)); background: var(--card); }
.more-jobs li strong { display: flex; align-items: center; gap: 9px; font-size: 17px; letter-spacing: -.025em; }
.more-jobs li strong .cat-icon { width: 17px; height: 17px; }
.more-jobs li span { color: var(--muted); font-size: 14px; line-height: 1.5; }

footer { display: flex; justify-content: space-between; gap: 28px; padding: 30px 0 42px; border-top: 1px solid var(--line); color: var(--muted); font-size: 12px; }

@media (max-width: 900px) {
  .intake-layout { grid-template-columns: 1fr; padding-top: 40px; }
}

@media (max-width: 640px) {
  .shell { width: min(100% - 28px, 1180px); }
  .topbar { min-height: 70px; gap: 8px; }
  .nav a:not(.credit-pill) { display: none; }
  /* Two pills now have to fit beside the wordmark on a 390px screen. The gaps
     and the type close up, and the account pill drops to its short form — same
     link, same destination, only as many words as there is room for.
     Descendant selectors for the same reason as the block above: the eight job
     pages carrying an inline copy of `.nav` and `.credit-pill` would otherwise
     win the tie on source order and keep their 28px desktop gap here. */
  .topbar .nav { gap: 8px; }
  .nav .credit-pill { padding: 8px 10px; font-size: 12px; white-space: nowrap; }
  .account-full { display: none; }
  .account-short { display: inline; }
  /* The buy pill drops the job's name and keeps its price. On a 1440px screen
     the name is orientation; on a 390px one it is the h1 four lines below,
     repeated in a pill that then wraps onto three lines and squeezes the way
     back in down to two. The number is the half that is not repeated anywhere
     else on the page. */
  .pill-job { display: none; }
  .job-form { padding: 14px; box-shadow: 5px 5px 0 var(--ink); }
  .primary-button { width: 100%; }
  .result-section { padding: 22px 18px; }
  .turn-head { padding: 12px 18px; }
  .turn-brief .turn-body, .turn-ask .turn-body, .turn-answer .turn-body { padding: 18px; }
  .turn-question .turn-body, .turn-opening .turn-body { padding: 16px 18px; }
  .turn-earlier > summary { padding: 11px 18px; }
  /* The two buttons under a question stop sharing a row: on a 390px screen a
     Next beside a Skip leaves neither wide enough to be pressed with a thumb,
     and Next is what the question is asking for, so it goes first and full
     width with Skip under it. */
  .step-next { flex: 1 1 100%; }
  .step-skip { flex: 1 1 100%; }
  .composer { padding: 16px 18px; box-shadow: 4px 4px 0 var(--ink); }
  /* The price sits above a full-width button rather than beside a short one.
     Both are still the last two things read before the points are spent. */
  .composer-send { width: 100%; }
  .composer-topup { display: block; text-align: center; }
  footer { flex-direction: column; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* --- the three themes ------------------------------------------------------

   Light, dark, and follow-the-system, out of ONE set of declarations.

   Every colour that differs between the two themes is written once, as
   light-dark(light, dark). Which half is used is decided by the root's USED
   COLOUR SCHEME, not by a media query: `color-scheme: light dark` on :root
   follows the operating system, and an explicit choice narrows it to a single
   scheme, which is what makes an explicit light choice beat a system that says
   dark, and the reverse.

   A media query cannot do that job, which is worth stating because the opposite
   is the natural thing to assume. `prefers-color-scheme` reports the OS setting
   and is NOT re-pointed by `color-scheme` — measured in the engine, not
   remembered. So a `@media (prefers-color-scheme: dark)` rule would go on firing
   underneath an explicit light choice, and the only way to stop it would be a
   `:not([data-theme="light"])` guard on every dark selector plus a second copy
   of every dark declaration for the explicit case. There is no second copy of
   anything here, which is the point.

   No JavaScript is involved in any of this. The pre-paint stamp in each page's
   head only sets data-theme; with no script, no storage, or no choice made,
   :root keeps `light dark` and the page follows the system exactly as it did
   before any of this existed.

   Each themed declaration is preceded by its plain light value. A browser too
   old for light-dark() drops the pair and keeps the plain one, so it renders
   the light theme intact rather than a page with no backgrounds — it loses
   automatic dark mode, it does not lose the page. */
:root[data-theme="light"] { color-scheme: only light; }
:root[data-theme="dark"] { color-scheme: only dark; }

.field input, .field textarea, .field select, .composer textarea,
.photo-field input[type="file"] { background: #fffefb; background: light-dark(#fffefb, #0f150c); }
.field input::placeholder, .field textarea::placeholder, .composer textarea::placeholder { color: #899087; color: light-dark(#899087, #7c887a); }
/* --text rather than a pair: it is --ink on paper, which is what these already
   said, and the lit ink on the unlit theme. */
.field input:focus, .field textarea:focus, .field select:focus, .composer textarea:focus { border-color: var(--text); }
.photo-list li, .side-card, .more-jobs li a, .job-ledger { background: rgba(255, 253, 248, .72); background: light-dark(rgba(255, 253, 248, .72), var(--card)); }
.unavailable {
  border-color: #ddbf75;
  border-color: light-dark(#ddbf75, rgba(243, 240, 230, .28));
  background: #fff5cf;
  background: light-dark(#fff5cf, #2a2410);
  color: #674c09;
  color: light-dark(#674c09, #f0dda4);
}
.consent-row input { accent-color: var(--ink); accent-color: light-dark(var(--ink), var(--acid)); }
.consent-row:hover { background: rgba(217, 255, 87, .18); background: light-dark(rgba(217, 255, 87, .18), rgba(217, 255, 87, .1)); }
.primary-button {
  background: var(--ink);
  background: light-dark(var(--ink), var(--acid));
  color: #fff;
  color: light-dark(#fff, var(--on-lime));
}
.primary-button:hover { background: var(--green); background: light-dark(var(--green), #eaffa0); }
.job-form, .loading-card, .composer { border-color: var(--ink); border-color: light-dark(var(--ink), rgba(243, 240, 230, .22)); }
/* The composer is the page's primary action on this screen, so it takes the
   same inversion the pay button takes: acid on ink reads as a button here and
   as a hole in the page on the unlit theme. */
.composer-send:not(:disabled) {
  background: var(--ink);
  background: light-dark(var(--ink), var(--acid));
  color: #fff;
  color: light-dark(#fff, var(--on-lime));
}
.composer-send:hover:not(:disabled) { background: var(--green); background: light-dark(var(--green), #eaffa0); }
/* --ink is a dark green, so an ink keyline on an unlit card is a keyline nobody
   can see. The one live control in this state has to be visible — and --text is
   already exactly "ink on paper, lit on ink", so it says it in one declaration.
   The hover keeps ink type because the fill under it is lime on both themes. */
.composer-topup { border-color: var(--text); }
.composer-topup:hover { color: var(--ink); }
/* Every one of these images is printed on a paper ground, which on the unlit
   theme makes it a lit rectangle on an unlit page.
   One hairline cannot hold that edge, and it was worth capturing a dark
   screenshot to find out: a light rule is invisible against #f7f3ea, and a dark
   rule is invisible against #12180f, so whichever single colour is picked
   disappears on the side that actually needed it. The edge is therefore built
   from both — a light rule facing the page and a darker one facing the paper —
   which is what the printed card this is meant to resemble actually has. */
.job-hero img { border-color: var(--line); border-color: light-dark(var(--line), rgba(243, 240, 230, .34)); }
.job-hero::after { border-color: rgba(22, 32, 25, .12); border-color: light-dark(rgba(22, 32, 25, .12), rgba(22, 32, 25, .5)); }
.copy-box { background: #f1eee4; background: light-dark(#f1eee4, #0f150c); }
.plain-text { color: #3d463d; color: light-dark(#3d463d, var(--muted)); }

/* ---------------------------------------------------------------------------
   The writer picker.

   Every option is styled IDENTICALLY apart from the selected outline. No rung
   is highlighted, badged as "best value", tinted, enlarged or ordered by
   anything but price, because the moment one of them looks like the intended
   answer the control stops being a choice and starts being a nudge. The only
   emphasis anywhere is the "Included" pill on the cheapest option — which
   points DOWN the ladder, not up. */
.tier-heading { margin: 0; font-size: 19px; font-weight: 900; letter-spacing: -.02em; }
.tier-lede { margin: 6px 0 0; color: var(--muted); font-size: 14px; line-height: 1.55; max-width: 56ch; }
.tier-options { display: grid; gap: 10px; margin: 16px 0 0; }
.tier-option {
  display: grid;
  grid-template-columns: 22px 1fr auto;
  gap: 14px;
  align-items: start;
  padding: 14px 16px;
  border: 1.5px solid var(--line);
  border-radius: 14px;
  background: var(--card);
  cursor: pointer;
  /* 44px minimum touch target, per the same rule .consent-row follows. */
  min-height: 44px;
}
.tier-option:hover { background: rgba(217, 255, 87, .12); background: light-dark(rgba(217, 255, 87, .12), rgba(217, 255, 87, .07)); }
.tier-option.is-selected { border-color: var(--text); }
.tier-option input { width: 20px; height: 20px; margin: 2px 0 0; accent-color: var(--ink); accent-color: light-dark(var(--ink), var(--acid)); }
.tier-text { display: grid; gap: 4px; }
.tier-name { font-size: 15.5px; font-weight: 900; letter-spacing: -.015em; }
.tier-included {
  display: inline-block;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(217, 255, 87, .55);
  background: light-dark(rgba(217, 255, 87, .55), rgba(217, 255, 87, .22));
  color: var(--green-ink);
  font-size: 10.5px;
  font-weight: 900;
  letter-spacing: .07em;
  text-transform: uppercase;
  vertical-align: 1px;
}
.tier-desc { color: var(--muted); font-size: 13.5px; line-height: 1.5; max-width: 46ch; }
/* Both figures, right-aligned into one column so they read as a pair. The cap
   is quieter than the typical price but never hidden: it is on screen before
   any click, which is the promise. */
.tier-price { text-align: right; font-size: 13px; font-weight: 850; white-space: nowrap; font-variant-numeric: tabular-nums; }
.tier-cap { display: block; margin-top: 3px; color: var(--muted); font-weight: 700; font-size: 11.5px; }
.tier-note {
  margin: 14px 0 0;
  padding: 12px 15px;
  border-left: 4px solid var(--acid);
  border-radius: 10px;
  background: rgba(22, 32, 25, .05);
  background: light-dark(rgba(22, 32, 25, .05), rgba(243, 240, 230, .06));
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.6;
  max-width: 62ch;
}
/* Narrow screens: the price moves under the description rather than squeezing
   the name into two words a line. */
@media (max-width: 560px) {
  .tier-option { grid-template-columns: 22px 1fr; }
  .tier-price { grid-column: 2; text-align: left; margin-top: 6px; }
  .tier-cap { display: inline; margin: 0 0 0 8px; }
}

/* The link to the receipts, inside the de-pressure note. Underlined and in the
   body colour rather than styled as a button: it is a footnote offering the
   whole bill, not a third thing competing with the two prices above it. */
.tier-receipts { color: inherit; font-weight: 800; text-decoration: underline; text-underline-offset: 2px; white-space: nowrap; }
