/*
 * PINTO — one stylesheet, three fronts.
 *
 * Mostly white, with a single accent that arrives at runtime from /session as `--accent`.
 * Nothing else here names a colour that belongs to a brand, which is what makes Gallo,
 * Blackvue and Aquavision the same build.
 *
 * ────────────────────────────────────────────────────────────────────────────
 * THE RESTRAINT IS THE DESIGN
 * ────────────────────────────────────────────────────────────────────────────
 *
 * A support page is used by somebody who already has a problem. Every element that is not
 * helping is in the way. So: one column, one accent, one weight of rule, and a great deal
 * of space.
 *
 * The accent appears in exactly two places — the send button and the focus
 * ring. Not on the question bubble, not on the chips, not on a border somewhere for
 * balance. Colour used sparingly reads as deliberate; colour used everywhere reads as
 * decoration, and decoration on a page about a broken speaker is noise.
 *
 * Type is the system stack, which is San Francisco on a Mac and Segoe on Windows — the face
 * the reader's own operating system uses, at the size their own settings imply. A webfont
 * would be one more thing to load before somebody can read a fault code.
 *
 * ────────────────────────────────────────────────────────────────────────────
 * LIGHT ONLY, DELIBERATELY
 * ────────────────────────────────────────────────────────────────────────────
 *
 * This is embedded on brand sites that are themselves light. Following the system theme
 * would put a dark panel on a white page and read as a rendering fault. Every colour is
 * stated explicitly so the page holds whatever ground it lands on.
 */

:root {
  /* Replaced at runtime from /session. Gallo blue is the sensible default of the three. */
  --accent: #009FE3;
  --accent-quiet: color-mix(in oklab, var(--accent) 8%, #FFFFFF);
  --accent-edge: color-mix(in oklab, var(--accent) 22%, #FFFFFF);

  --ground: #FFFFFF;
  --raised: #F5F5F7;          /* the one grey. Cool, barely there. */
  --line: #E8E8ED;
  --ink: #1D1D1F;             /* near-black, never pure */
  --ink-2: #424245;
  --muted: #86868B;

  --gap: clamp(1.25rem, 4vw, 2rem);
  --measure: 40rem;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--ground);
  color: var(--ink-2);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  font-size: 17px;
  line-height: 1.55;
  letter-spacing: -0.011em;   /* the tightening that makes system type look set rather than default */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.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;
}

/*
 * ONE COLUMN, VERTICALLY CENTRED UNTIL THERE IS SOMETHING TO READ.
 *
 * The first version pinned the composer to the bottom of the viewport and let the thread
 * hug the top, which on a desktop left a large void between the two and read as an
 * unfinished page. Before a question is asked there is no conversation to anchor, so the
 * whole thing sits in the middle of the screen like a search field. Once an answer exists
 * the column grows and the composer settles to the bottom.
 */
/*
 * THE COMPOSER IS ALWAYS AT THE BOTTOM.
 *
 * It used to float wherever the column's centring left it, which meant the one control on
 * the page moved depending on how much else was on screen. A text field that is somewhere
 * different every time you look is a text field you have to find.
 *
 * So the shell fills the window, `.middle` absorbs the slack, and the composer sits after
 * it — pinned to the bottom whether there is a conversation or not.
 */
.shell {
  min-height: 100%;
  max-width: var(--measure);
  margin: 0 auto;
  padding: calc(var(--gap) * 3) var(--gap) calc(var(--gap) + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

/*
 * THE COMPOSER STAYS WHERE IT IS. THE CONVERSATION SCROLLS.
 *
 * The shell used to grow with the thread, so every answer pushed the input further down
 * the page and the reader chased it. Now the shell is exactly the window, `.middle` is
 * the scrolling region, and the composer never moves. A new turn scrolls the region to its
 * end; the reader can scroll back up through the conversation without the input leaving.
 */
.shell { height: 100vh; height: 100dvh; }

/*
 * THE TEXT STAYS IN THE MIDDLE. THE CARDS GO OUT TO THE RIGHT.
 *
 * Once a card exists the shell takes the whole window width, but the reading column does
 * not move: every answer is a three-track grid — a spare track, the 40rem measure, a
 * spare track — and the cards sit in the right-hand spare. The question bubbles, the
 * composer and the notice are held to the measure and centred, so the column a reader
 * has been looking at is exactly where it was before the first card arrived.
 */
@media (min-width: 1200px) {
  .shell.has-cards { max-width: none; width: 100%; }
  .shell.has-cards .stage, .shell.has-cards .intro, .shell.has-cards .composer,
  .shell.has-cards .notice, .shell.has-cards .turn.you {
    max-width: var(--measure); width: 100%; margin-inline: auto;
  }
  .shell.has-cards .turn.you { align-self: stretch; }
}

/* Centres the openers in whatever room is left above the composer. With a
   conversation it hugs the bottom instead, so the newest answer is next to the input. */
.middle {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2rem;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
  padding-top: 0.5rem;
}
/*
 * Bottom-anchored WITHOUT `justify-content: flex-end`, which makes the top of an
 * overflowing flex column unreachable by scrolling. `margin-top: auto` on the thread
 * pushes it to the floor of the region while it is short, and behaves like an ordinary
 * scrolling column once it is not.
 */
.shell.has-thread .middle { justify-content: flex-start; }
.shell.has-thread .thread { margin-top: auto; }
/*
 * Once there is a conversation it sits at the BOTTOM, not the top.
 *
 * Anchoring to the top left a short exchange stranded against a large void above the
 * composer — the answer at the ceiling, the input at the floor, and nothing between. Growing
 * upward from the composer is what every messaging app does, and for the same reason: the
 * newest thing and the thing you type into belong next to each other.
 */

/* ── brand bar ─────────────────────────────────────────────────────────── */

/*
 * Fixed to the WINDOW, not the column.
 *
 * It was absolutely positioned inside `.shell`, which is a 40rem column centred in the
 * viewport — so on a wide screen the mark sat a third of the way in and read as part of the
 * conversation rather than as the page identifying itself.
 */
.brandbar {
  position: fixed;
  top: var(--gap); left: var(--gap);
  z-index: 2;
  display: flex; align-items: center; gap: 0.6rem;
}
/* Sized by HEIGHT, with the width following the artwork.
   Gallo's mark is a square sphere; Aquavision's is a wide arc with a droplet on it (206×83).
   A fixed 26×26 box letterboxed the second one to about ten pixels tall, which read as a
   smudge rather than a logo. Capped so an unusually wide mark cannot crowd the wordmark. */
.mark { height: 26px; width: auto; max-width: 76px; object-fit: contain; display: block; }
.wordmark {
  font-size: 0.95rem; font-weight: 600; letter-spacing: -0.012em;
  color: var(--ink);
}

/* ── the stage ─────────────────────────────────────────────────────────── */

.stage {
  display: flex; flex-direction: column; align-items: center;
  gap: 1.1rem; text-align: center;
}
.shell.has-thread .stage { display: none; }

@media (prefers-reduced-motion: reduce) {
  .shell { transition: none; }
}

h1, .wordmark { text-wrap: balance; }
.sub { font-size: 1.05rem; color: var(--muted); margin: 0; max-width: 34ch; text-wrap: balance; }

/* ── the opening ───────────────────────────────────────────────────────── */

.intro { display: flex; flex-direction: column; align-items: center; gap: 1rem; }
.intro.gone { display: none; }
.chips { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; }
.chip {
  font: inherit;
  font-size: 0.9rem;
  padding: 0.5rem 0.95rem;
  border: 1px solid var(--line);
  border-radius: 980px;              /* Apple's pill radius — effectively "fully round" */
  background: var(--ground);
  color: var(--ink-2);
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.12s ease;
}
.chip:hover { background: var(--raised); border-color: var(--line); }
.chip:active { transform: scale(0.97); }
.chip:focus-visible { outline: 3px solid var(--accent-edge); outline-offset: 2px; }

/* ── thread ────────────────────────────────────────────────────────────── */

.thread { display: flex; flex-direction: column; gap: 1.75rem; padding-bottom: 0.5rem; }
.thread:empty { display: none; }

.turn { display: flex; flex-direction: column; gap: 0.6rem; animation: rise 0.35s cubic-bezier(.4,0,.2,1); }
.turn.you { align-items: flex-end; }

/*
 * An answer is a grid: the text (and its sources) in the first column, the cards in the
 * second. On a narrow screen the second column folds under the first as a strip.
 */
.turn.pinto { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.6rem; }
.turn__main { display: flex; flex-direction: column; gap: 0.6rem; min-width: 0; }

/*
 * THE ASIDE IS A STACK AT EVERY WIDTH, and that has to be said HERE rather than only in
 * the wide-screen rule below.
 *
 * It was: a plain block by default, turned into a `flex` column with `gap: 1rem` inside
 * `@media (min-width: …)`. So on a phone — the width most of these are read at — the
 * product cards, the returns card and the talk-to-us card stacked edge to edge with
 * nothing between them and read as one undifferentiated slab.
 *
 * The gap belongs to the stack, not to the breakpoint. The media query below is for the
 * things that genuinely are about having room: the sticky column, its own scroll, the
 * width cap.
 */
.turn__aside { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.turn__aside:empty { display: none; }
@media (min-width: 1200px) {
  .shell.has-cards .turn.pinto {
    /* spare | the measure | spare — symmetric, so the text is centred; the cards use the right spare. */
    grid-template-columns: minmax(0, 1fr) minmax(0, var(--measure)) minmax(0, 1fr);
    column-gap: 0;
    align-items: start;
  }
  .shell.has-cards .turn__main { grid-column: 2; }
  .shell.has-cards .turn__aside {
    grid-column: 3;
    justify-self: start;
    width: min(21rem, 100%);
    padding-left: clamp(1.5rem, 3vw, 4rem);
    box-sizing: content-box;
    /* Travels with its answer, and lingers in view while a long answer scrolls past —
       moving at its own pace rather than the text's. */
    position: sticky; top: 0.5rem;
    display: flex; flex-direction: column; gap: 1rem;
    /*
     * ITS OWN SCROLL. The column is capped to the conversation region's height — the
     * page sets `--middle-h` — and scrolls inside itself, so a range of twelve cards is
     * read by scrolling the cards, not the text. `overscroll-behavior: contain` keeps a
     * wheel over the cards from spilling into the conversation once they reach the end.
     */
    max-height: calc(var(--middle-h, 70vh) - 1rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--line) transparent;
    padding-right: 0.5rem;
    padding-bottom: 0.5rem;
    /* Room for the lift on hover and the entrance, which would otherwise be clipped at the edges. */
    padding-top: 6px;
    margin-top: -6px;
  }
  .shell.has-cards .turn__aside > .pcard { flex: none; }
}

@keyframes rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .turn { animation: none; } }

.bubble {
  max-width: 85%;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* The question gets a shape. The answer does not — it is the page's content, not a message
   in a chat app, and boxing it makes the reader work harder to read it. */
.turn.you .bubble {
  padding: 0.65rem 1.05rem;
  border-radius: 20px;
  background: var(--raised);
  color: var(--ink);
  font-size: 0.95rem;
}
.turn.pinto .bubble {
  color: var(--ink);
  font-size: 1.0625rem;
  line-height: 1.6;
  max-width: 100%;
  /* Rendered as blocks by `renderAnswer`, not as a pre-wrapped string. */
  white-space: normal;
}

/* ── the shape of an answer ────────────────────────────────────────────── */

/* Paragraphs, lists and a specification grid. The model is asked to write "Label: value"
   lines and dashed lists; the page turns those into structure rather than printing the
   punctuation. A wall of text was the complaint; this is the shape instead. */
.bubble > p { margin: 0 0 0.75rem; }
.bubble > p:last-child { margin-bottom: 0; }
.bubble > ul, .bubble > ol { margin: 0 0 0.85rem; padding-left: 1.3rem; }
.bubble > ul:last-child, .bubble > ol:last-child { margin-bottom: 0; }
.bubble li { margin: 0.2rem 0; }
.bubble li::marker { color: var(--muted); }
.bubble strong { font-weight: 600; color: var(--ink); }
.bubble .specs {
  display: grid;
  grid-template-columns: minmax(7rem, max-content) 1fr;
  column-gap: 1.25rem;
  row-gap: 0;
  margin: 0.35rem 0 0.9rem;
  font-size: 0.96rem;
  border-top: 1px solid var(--line);
}
.bubble .specs:last-child { margin-bottom: 0; }
.bubble .specs dt, .bubble .specs dd {
  margin: 0; padding: 0.45rem 0;
  border-bottom: 1px solid var(--line);
}
.bubble .specs dt { color: var(--muted); }
.bubble .specs dd { color: var(--ink); font-variant-numeric: tabular-nums; }
@media (max-width: 560px) {
  .bubble .specs { grid-template-columns: 1fr; }
  .bubble .specs dt { padding-bottom: 0; border-bottom: 0; font-size: 0.8rem; }
}
.turn.pinto.unanswered .bubble { color: var(--muted); }

/* ── sources ───────────────────────────────────────────────────────────── */

/* Provenance, not furniture. One chip is shown and the rest are behind "+N" — see
   renderSources. Smaller than the answer by a clear step, so the eye reaches the answer
   first and finds the source only when it goes looking. */
.sources { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.source {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font: inherit;                     /* a <button> would otherwise bring its own */
  font-size: 0.72rem;
  line-height: 1.35;
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 980px;
  color: var(--muted);
  text-decoration: none;
  background: var(--ground);
  transition: color 0.2s ease, border-color 0.2s ease;
}
.source[hidden] { display: none; }    /* `hidden` loses to `display: inline-flex` above */
.source.more { cursor: pointer; font-variant-numeric: tabular-nums; }
.source.more:hover { color: var(--ink-2); border-color: var(--muted); }
.source:hover { color: var(--ink-2); border-color: var(--muted); }
.source:focus-visible { outline: 3px solid var(--accent-edge); outline-offset: 2px; }

/*
 * Freshness is a DOT, not a word.
 *
 * The first version printed the state as uppercase text — a source chip reading
 * "Micro Ti Owner's Manual UNKNOWN", which is the least important thing on the row shouting
 * the loudest, and which a customer reads as something having gone wrong.
 *
 * It is still shown, because a document nobody has dated is worth knowing about. It is
 * shown as a small mark with a title attribute, so it is available to anybody who looks and
 * invisible to everybody who does not.
 */
.source::before {
  content: "";
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--line);
  flex: none;
}
.source[data-freshness="fresh"]::before { background: color-mix(in oklab, var(--accent) 60%, #FFFFFF); }
.source[data-freshness="unknown"]::before { background: #D2D2D7; }
/* evidence kinds: an NWX-verified note is marked with the accent; a field aggregate (staff only) with a hollow dot */
.source[data-kind="assertion"] { border-color: var(--accent); }
.source[data-kind="assertion"]::before { background: var(--accent); }
.source[data-kind="field"]::before { background: transparent; box-shadow: inset 0 0 0 2px var(--muted); }
/* "did this help?" */
.feedback { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; margin-top: 0.5rem; font-size: 0.78rem; color: var(--muted); }
.feedback__btn { font: inherit; font-size: 0.74rem; padding: 0.15rem 0.55rem; border: 1px solid var(--line, #D2D2D7); border-radius: 999px; background: transparent; color: inherit; cursor: pointer; }
.feedback__btn:hover { color: var(--ink-2); border-color: var(--muted); }
.feedback__btn:disabled { opacity: 0.5; cursor: default; }
.source[data-freshness="stale"]::before { background: #E8B84B; }
/* The expander borrows the chip's shape but is not a source, so it carries no freshness
   dot — a grey dot beside "+5" would read as a fifth document of unknown age. */
.source.more::before { display: none; }

/* ── composer ──────────────────────────────────────────────────────────── */

.composer {
  display: flex; gap: 0.5rem; align-items: center;
  border: 1px solid var(--line);
  border-radius: 980px;
  padding: 0.35rem 0.35rem 0.35rem 1.15rem;
  background: var(--ground);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.composer:focus-within {
  border-color: var(--accent-edge);
  box-shadow: 0 0 0 4px var(--accent-quiet);
}
/* No auto margin — the column is bottom-anchored, so the composer is already where the
   conversation ends. */

.composer input {
  flex: 1; min-width: 0;
  border: 0; outline: none; background: none;
  font: inherit;
  /* 17px, so iOS never zooms the page in on focus and then refuses to zoom back out. */
  font-size: 17px;
  color: var(--ink);
  padding: 0.55rem 0;
}
.composer input::placeholder { color: var(--muted); }

#mic {
  flex: none;
  width: 38px; height: 38px;
  border: 0; border-radius: 50%;
  background: none; color: var(--muted);
  display: grid; place-items: center; cursor: pointer;
  transition: color 0.2s ease, background 0.2s ease;
}
#mic:hover { color: var(--ink-2); background: var(--raised); }
#mic:focus-visible { outline: 3px solid var(--accent-edge); outline-offset: 2px; }

/* Arming: the browser is asking for the microphone and has not granted it yet. That wait is
   real and is shown rather than hidden, so a slow permission prompt does not look like a
   dead button. */
#mic[data-state="arming"] { color: var(--muted); animation: micPulse 0.7s ease-in-out infinite; }
/* Recording: the one place on this page a colour other than the accent is used, because
   "you are being recorded" must not be mistakable for "this is the send button". */
#mic[data-state="recording"] { color: #D7263D; background: rgba(215, 38, 61, 0.08); }
#mic[data-state="recording"] { animation: micPulse 1.6s ease-in-out infinite; }

@keyframes micPulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

#send {
  flex: none;
  width: 38px; height: 38px;
  border: 0; border-radius: 50%;
  background: var(--accent); color: #FFFFFF;
  display: grid; place-items: center; cursor: pointer;
  transition: opacity 0.2s ease, transform 0.12s ease;
}
#send:active { transform: scale(0.94); }
#send:disabled { opacity: 0.3; cursor: default; }
#send:focus-visible { outline: 3px solid var(--accent-edge); outline-offset: 2px; }

.notice {
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--muted);
  margin: 0;
  text-align: center;
  text-wrap: pretty;
}
.shell.has-thread .notice { text-align: left; }
.notice button.linklike {
  font: inherit; color: var(--ink-2); background: none; border: 0; padding: 0;
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.notice button.linklike:hover { color: var(--ink); }

@media (max-width: 560px) {
  .bubble { max-width: 92%; }
  .shell { gap: 1.5rem; }
}

/* ─────────────────────────────────────────────────────────────────────────────
 * THE HUB — the landing page, when no brand claims the host.
 *
 * Same restraint, same tokens, one difference: here the accent is per CARD rather than
 * per page, because this is the one screen in Pinto that shows all three brands at once.
 * Each card sets its own `--accent` and uses it in exactly one place — the edge that
 * appears on hover — so three brands sit together without the page becoming a swatch.
 * ───────────────────────────────────────────────────────────────────────────── */

.hub-body { height: auto; min-height: 100%; }

.hub {
  max-width: 58rem;
  margin: 0 auto;
  padding: clamp(3rem, 10vh, 6rem) var(--gap) var(--gap);
  display: flex;
  flex-direction: column;
  gap: clamp(2rem, 6vh, 3.25rem);
}

.hub-head { text-align: center; display: flex; flex-direction: column; gap: 0.6rem; align-items: center; }
.hub-head h1 {
  margin: 0;
  font-size: clamp(1.9rem, 5vw, 2.6rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.hub-head .sub { max-width: 38ch; }

.cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* Three across on a desk, one on a phone, two in between — without a media query, so
     there is no breakpoint to get wrong at some width nobody tested.
     13rem rather than 15: at a ~780px viewport the wider minimum wraps three brands to
     2+1, which reads as one card having been added later. */
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 1rem;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  height: 100%;
  padding: 1.4rem;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--ground);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
.card:hover {
  border-color: var(--accent-edge);
  box-shadow: 0 1px 2px rgb(0 0 0 / 4%), 0 8px 24px rgb(0 0 0 / 6%);
  transform: translateY(-2px);
}
.card:active { transform: translateY(0); }
.card:focus-visible { outline: 3px solid var(--accent-edge); outline-offset: 3px; }

.card-top { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; min-height: 30px; }
.card-mark { height: 24px; width: auto; max-width: 90px; object-fit: contain; display: block; }
.card-mark-fallback {
  display: grid;
  place-items: center;
  width: 26px; height: 26px;
  border-radius: 7px;
  background: var(--accent);
  color: #FFFFFF;
  font-size: 0.85rem;
  font-weight: 600;
}
.card-owner {
  font-size: 0.7rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}

.card-name { margin: 0.2rem 0 0; font-size: 1.12rem; font-weight: 600; color: var(--ink); letter-spacing: -0.01em; }
.card-holdings { margin: 0; font-size: 0.85rem; color: var(--ink-2); }

/* Amber would read as an alert. This is a note about completeness, not a fault. */
.card-caution { margin: 0; font-size: 0.8rem; color: var(--muted); font-style: italic; }

.card-host {
  margin: auto 0 0;
  padding-top: 0.7rem;
  font-size: 0.78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.card-skeleton {
  height: 11.5rem;
  border-radius: 16px;
  background: var(--raised);
  animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
@media (prefers-reduced-motion: reduce) {
  .card-skeleton { animation: none; }
  .card { transition: none; }
  .card:hover { transform: none; }
}

.hub-error { margin: 0; text-align: center; color: var(--ink-2); }
.hub-foot { text-align: center; }
.hub-foot p { margin: 0; font-size: 0.8rem; color: var(--muted); }
.hub-foot code { font-size: 0.95em; background: var(--raised); padding: 0.1em 0.4em; border-radius: 5px; }


/* ─────────────────────────────────────────────────────────────────────────────
 * PRODUCT CARDS — beside an answer on a wide screen, a strip beneath it otherwise.
 *
 * The card is the brochure site's `.pcard`, translated into this page's tokens: a white
 * panel with a photograph on top and the name, a small-caps tag, the price and two pills
 * beneath. The register is the site's — a lift under the pointer, the shot scaling a
 * hair, the photograph fading in when its file lands — and slower than the text: an
 * answer arrives in a third of a second; a card eases in over well over one, after a
 * beat, so it reads as an aside settling into place rather than a panel snapping open.
 * ───────────────────────────────────────────────────────────────────────────── */

:root { --ease: cubic-bezier(.2, .7, .2, 1); --ease-in-soft: cubic-bezier(.16, .84, .24, 1); }

.pcard {
  display: flex;
  flex-direction: column;
  background: var(--ground);
  border: 1px solid var(--line);
  border-radius: 18px;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  animation: cardIn 1.4s var(--ease-in-soft) both;
  animation-delay: calc(320ms + var(--i, 0) * 180ms);
  transition: transform 0.6s var(--ease), box-shadow 0.6s var(--ease), border-color 0.4s ease;
  will-change: transform, opacity;
}
.pcard:hover {
  transform: translateY(-4px);
  border-color: var(--accent-edge);
  box-shadow: 0 1px 2px rgb(0 0 0 / 4%), 0 18px 44px rgb(16 17 20 / 9%);
}

/* A card that was already on screen is MOVED beside the newest answer, not shown twice.
   It arrives without the entrance — it did not just appear, it followed the conversation. */
.pcard.is-moved { animation: none; }

@keyframes cardIn {
  0%   { opacity: 0; transform: translateY(28px) scale(0.97); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: none; }
}

/* The shot: white ground, the photograph multiplied onto it the way the site does, so a
   product cut out on white sits on the card without a visible rectangle. */
.pcard__shot {
  display: flex; align-items: center; justify-content: center;
  background: #FFFFFF;
  padding: 1.1rem 1.1rem 0.4rem;
  min-height: 9.5rem;
  overflow: hidden;
}
.pcard__shot img {
  display: block;
  width: 100%;
  max-height: 14rem;
  object-fit: contain;
  mix-blend-mode: multiply;
  opacity: 0;
  transform: scale(1.04);
  transition: opacity 1.1s var(--ease-in-soft), transform 1.4s var(--ease-in-soft);
}
.pcard__shot img.is-loaded { opacity: 1; transform: none; }
.pcard:hover .pcard__shot img.is-loaded { transform: scale(1.045); }
.pcard__shot--none { min-height: 0; padding: 0.6rem 0 0; }

.pcard__meta { padding: 0.9rem 1.25rem 1.2rem; display: flex; flex-direction: column; gap: 0.35rem; }
.pcard__kicker {
  font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
}
.pcard__name {
  margin: 0; font-size: 1.15rem; font-weight: 600; letter-spacing: -0.012em; color: var(--ink);
  line-height: 1.25;
}
.pcard__tag {
  font-size: 0.85rem; color: var(--muted);
  font-variant: small-caps; letter-spacing: 0.03em;
}
.pcard__price {
  margin: 0.35rem 0 0; font-size: 1rem; font-weight: 500; color: var(--ink);
  font-variant-numeric: tabular-nums;
  display: flex; flex-direction: column; gap: 0.1rem;
}
.pcard__price small { font-size: 0.72rem; font-weight: 400; color: var(--muted); }

.pcard__links { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-top: 0.7rem; }
.pill {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font: inherit; font-size: 0.82rem; font-weight: 500;
  padding: 0.45rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: 980px;
  color: var(--ink);
  text-decoration: none;
  background: var(--ground);
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.pill:hover { background: var(--raised); border-color: var(--muted); }
.pill--ink { background: var(--ink); border-color: var(--ink); color: #FFFFFF; }
.pill--ink:hover { background: #000; border-color: #000; }
.pill:focus-visible { outline: 3px solid var(--accent-edge); outline-offset: 2px; }
.pill:disabled { opacity: 0.4; cursor: default; }
/* The arrow in a pill slides on hover — the site's gesture. */
.pill__arrow { display: inline-block; transition: transform 0.38s var(--ease); }
.pill:hover .pill__arrow { transform: translateX(3px); }

/* ── talk to the team ──────────────────────────────────────────────────────
 *
 * Offered beside an answer Pinto could not settle — a price, stock, a warranty, or a
 * gap in what it holds. The same card shape, no photograph, the brand's accent as a thin
 * top rule so it reads as an invitation rather than another product. Pressing the pill
 * turns the card into a small chat with the team, right there, with the conversation
 * so far sent along so nobody repeats themselves.
 */
.pcard--team { border-top: 3px solid var(--accent); }
.pcard--team .pcard__line, .pcard--rma .pcard__line { margin: 0.2rem 0 0; font-size: 0.92rem; color: var(--ink-2); line-height: 1.5; }

/*
 * The RMA card sits beside the team card and must not compete with it. The team card's
 * accent rule says "a person"; this one is the same card in grey, because posting a camera
 * away is a heavier step than starting a chat and should not be the brighter of the two.
 */
.pcard--rma { border-top: 3px solid var(--line); }

.teamchat { display: flex; flex-direction: column; gap: 0.7rem; padding: 0.9rem 1.25rem 1.2rem; }
.teamchat__head { display: flex; flex-direction: column; gap: 0.2rem; }
.teamchat__note { margin: 0; font-size: 0.8rem; color: var(--muted); line-height: 1.45; }
.teamchat label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.75rem; color: var(--muted); }
.teamchat input, .teamchat textarea {
  font: inherit; font-size: 0.95rem; color: var(--ink);
  border: 1px solid var(--line); border-radius: 12px; background: var(--ground);
  padding: 0.55rem 0.75rem; width: 100%;
}
.teamchat textarea { min-height: 5.5rem; resize: vertical; }
.teamchat input:focus-visible, .teamchat textarea:focus-visible {
  outline: none; border-color: var(--accent-edge); box-shadow: 0 0 0 4px var(--accent-quiet);
}
.teamchat__row { display: flex; gap: 0.5rem; align-items: center; justify-content: space-between; }
.teamchat__status { margin: 0; font-size: 0.78rem; color: var(--muted); }
.teamchat__thread { display: flex; flex-direction: column; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
.teamchat__msg { font-size: 0.92rem; line-height: 1.5; padding: 0.55rem 0.8rem; border-radius: 14px; max-width: 92%; white-space: pre-wrap; overflow-wrap: anywhere; }
.teamchat__msg--customer { align-self: flex-end; background: var(--raised); color: var(--ink); }
.teamchat__msg--team { align-self: flex-start; border: 1px solid var(--line); color: var(--ink); }
.teamchat__msg small { display: block; font-size: 0.7rem; color: var(--muted); margin-top: 0.2rem; }

/* Narrow: the aside is a horizontal strip beneath the answer. */
@media (max-width: 1199px) {
  .turn__aside {
    display: flex; gap: 0.85rem;
    overflow-x: auto;
    padding: 0.25rem 0 0.6rem;
    margin: 0.25rem 0 0;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .turn__aside::-webkit-scrollbar { display: none; }
  .turn__aside .pcard { flex: 0 0 min(17rem, 78vw); scroll-snap-align: start; }
  .turn__aside .pcard--team, .turn__aside .pcard.is-chat { flex-basis: min(24rem, 92vw); }
  .turn__aside .pcard__shot { min-height: 7rem; }
  .turn__aside .pcard__shot img { max-height: 9rem; }
}

@media (prefers-reduced-motion: reduce) {
  .pcard { animation: none; transition: none; }
  .pcard:hover { transform: none; }
  .pcard__shot img { transition: none; transform: none; opacity: 1; }
  .pill__arrow { transition: none; }
}

/* ── the symptom menu, and the case it opens ─────────────────────────────────
 *
 * The case tier's front door on the answer panel (build plan §6.1). It sits under the
 * openers because the composer is still the primary thing: a QUESTION goes there, and this
 * is for when something is actually broken.
 *
 * Deliberately quieter than the composer and louder than the chips — a middle weight,
 * because it is a real second path rather than a suggestion.
 */
.chip--symptom { background: var(--raised); }
.chip--symptom:hover { background: var(--ground); border-color: var(--muted); }

.caseform {
  display: flex; flex-direction: column; gap: 0.7rem;
  width: 100%; max-width: 26rem; margin: 1.25rem auto 0;
  padding: 1.1rem; border: 1px solid var(--line); border-radius: 14px; text-align: left;
}
.caseform__title { margin: 0; font-size: 1.05rem; font-weight: 600; color: var(--ink); letter-spacing: -0.012em; }
.caseform__lead { margin: 0; font-size: 0.85rem; color: var(--muted); }
.caseform__field { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.8rem; color: var(--muted); }
.caseform__field input, .caseform__field textarea {
  font: inherit; min-height: 44px; padding: 0.55rem 0.7rem;
  border: 1px solid var(--line); border-radius: 10px; background: var(--ground); color: var(--ink); width: 100%;
}
.caseform__field textarea { min-height: 4.5rem; resize: vertical; line-height: 1.5; }
.caseform__field input:focus-visible, .caseform__field textarea:focus-visible { outline: 3px solid var(--accent-edge); outline-offset: 1px; border-color: var(--accent); }
.caseform__go { align-self: flex-start; margin-top: 0.2rem; }
.caseform__go[disabled] { opacity: 0.5; cursor: default; }
.caseform__why { margin: 0; font-size: 0.82rem; color: var(--ink-2); min-height: 1.1em; }

/* ── the symptom rail ────────────────────────────────────────────────────────
 *
 * Beside the conversation where there is room, collapsed beneath it where there is not.
 * It is an OPENER: once a conversation is under way it goes, because the thing on screen
 * is then the thing being talked about, and a menu of twelve other problems beside it is
 * noise. The list is still reachable — reloading brings it back, and the composer answers
 * the same sentences anyway.
 */
.rail {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0 auto;
  width: 100%;
  max-width: var(--measure);
}
.rail[hidden] { display: none; }
.shell.has-thread .rail { display: none; }

.rail__lead {
  margin: 0 0 0.1rem;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.rail__groups { display: flex; flex-direction: column; gap: 0.4rem; }

.railgroup {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--ground);
  overflow: hidden;
}
.railgroup__head {
  cursor: pointer;
  padding: 0.7rem 0.9rem;
  font-size: 0.92rem;
  color: var(--ink);
  min-height: 44px;
  display: flex;
  align-items: center;
  list-style: none;
}
.railgroup__head::-webkit-details-marker { display: none; }
/* Never colour alone, and never an icon font: the twisty is a character that rotates. */
.railgroup__head::after {
  content: "+";
  margin-left: auto;
  color: var(--muted);
  font-size: 1.05rem;
  line-height: 1;
}
.railgroup[open] .railgroup__head::after { content: "–"; }
.railgroup__head:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }
.railgroup__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0 0.9rem 0.9rem;
}
/*
 * "I ALREADY HAVE A CASE" — a real control, not a bare browser button.
 *
 * `.linklike` is styled only inside `.notice`, so on its own this rendered with the user
 * agent's default chrome: a grey box with a border, sitting under four carefully drawn
 * groups. It is a DOOR, not a link — somebody arriving with a reference in an email wants
 * to find it — so it is given the same shape as the groups above it and a full-width hit
 * area, rather than being shrunk to underlined text.
 */
.rail__resume {
  font: inherit;
  font-size: 0.9rem;
  width: 100%;
  min-height: 44px;
  margin-top: 0.3rem;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--raised);
  color: var(--ink-2);
  text-align: left;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.rail__resume:hover { background: var(--ground); border-color: var(--muted); color: var(--ink); }
.rail__resume:focus-visible { outline: 3px solid var(--accent-edge); outline-offset: 2px; }

@media (min-width: 1200px) {
  /*
   * OUT OF THE READING COLUMN ENTIRELY. Fixed to the right of the window rather than
   * squeezed into the measure, so the conversation keeps the middle and its width never
   * changes when the rail appears or goes.
   */
  .rail {
    position: fixed;
    right: clamp(1rem, 3vw, 2.5rem);
    top: 50%;
    transform: translateY(-50%);
    width: 17rem;
    max-width: 17rem;
    margin: 0;
    max-height: 80vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--line) transparent;
  }
}

/* ── a guided check, in the thread ───────────────────────────────────────────
 *
 * The case page's cadence, borrowed: why this step and what it rests on, quieter, above
 * the question itself. Same wording, same order, so the two surfaces are one product.
 */
.stepwhy { margin: 0; font-size: 0.88rem; color: var(--muted); line-height: 1.5; }
.stepcite { margin: 0; font-size: 0.8rem; color: var(--muted); line-height: 1.45; }
.stepwarn {
  margin: 0.2rem 0 0;
  padding: 0.6rem 0.8rem;
  border-radius: 10px;
  background: var(--raised);
  color: var(--ink-2);
  font-size: 0.88rem;
}
.stepdone { margin: 0.3rem 0 0; font-size: 0.88rem; color: var(--muted); }

.stepoptions { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-top: 0.2rem; }
.chip--option { text-align: left; }
.chip--option[disabled] { opacity: 0.45; cursor: default; }

.steprecord { display: flex; flex-direction: column; gap: 0.5rem; align-items: flex-start; margin-top: 0.6rem; }

/*
 * "STOP THE CHECKS" — the way out of a walk, and it has to look like one.
 *
 * Same problem as the resume button: `.linklike` is only styled inside `.notice`, so this
 * was a default browser button in the middle of the conversation. It must be QUIETER than
 * the options it sits under — nobody should be nudged towards abandoning a diagnostic
 * half-way — but it must not be hard to find either, because the one rule the page keeps
 * is that a person is never trapped. So: plain text at the muted weight, underlined so it
 * reads as an action, with a 44px target that does not show as a box.
 */
.stepstop {
  font: inherit;
  font-size: 0.85rem;
  align-self: flex-start;
  margin-top: 0.4rem;
  padding: 0.35rem 0;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  border: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--line);
}
.stepstop:hover { color: var(--ink); text-decoration-color: currentColor; }
.stepstop:focus-visible { outline: 3px solid var(--accent-edge); outline-offset: 2px; border-radius: 6px; }
.stepcase { margin-top: 0.8rem; }

/* ── the offer ───────────────────────────────────────────────────────────────
 *
 * Under the answer, never instead of it. Accented enough to be seen and quiet enough not
 * to read as the only thing on offer — it is a question, and "no" is a real answer to it.
 */
.flowoffer {
  margin-top: 0.7rem;
  padding: 0.9rem 1rem;
  border: 1px solid var(--accent-edge, var(--line));
  border-radius: 12px;
  background: var(--accent-quiet, var(--raised));
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.flowoffer__line { margin: 0; color: var(--ink-2); font-size: 0.95rem; line-height: 1.5; }
.flowoffer__row { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; }
