/* Chat app, design tokens, reset and base primitives. Loaded after tokens.css. */
/* ============================================================================
   YC Parallax, MODERN frontend (index-v2.html). Same app + features as
   index.html; this is a redesigned visual layer only. Brand-strict: Satoshi
   everywhere, orange (#F06731 / #C05227 + orange-scale) the only accent, the
   neutral grey scale carries all structure, #0A0A0A brand-black (never #000).
   Every class/id below matches the shared JS, so behaviour is untouched.
   ============================================================================ */
:root {
  --sidebar: #F2F2F2;
  --bubble-bot: #F2F2F2;
  /* The composer pill is WHITE (#FFFFFF). It used to be #F2F2F2 because it sat on the
     white chat card and needed to read as a surface-2 element on it. The card is gone
     and the composer now sits directly on the shell's #F2F2F2, so grey-on-grey would
     make the pill vanish -- white is what keeps the surface alternation intact. */
  --field-bg: #FFFFFF;
  --drawer-w: min(620px, 94vw);
  --sidebar-w: 274px;
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 20px; --sp-6: 28px;
  /* ---- corner radius: exactly four values, nothing in between ----------------
       --r-sm     16px   The TIGHT corner, for the two places 24px is too round:
                         the empty state's quick-prompt cards, and the Settings
                         panel. Settings is the load-bearing case -- its body
                         scrolls, and a 40px corner arced across the bottom of the
                         scrollbar track, so the scrollbar looked clipped where it
                         met the panel edge. 16px pulls the arc back clear of it.
       --r        24px   the default corner. Buttons, inputs, message bubbles,
                         artifact cards, tooltips, code blocks.
       --r-xl     40px   LARGE SURFACES only: the sidebar card, the search modal,
                         the confirm modal, the artifact drawer, the collapsed
                         rail. Reserved for things that contain other things.
       --r-pill  100px   Pills and circles: the composer, the search field, filter
                         chips, avatars, status dots, the send button. 100px is far
                         more than half the height of any of them, and a radius is
                         clamped to half the box, so it renders as a true pill or
                         circle at any size.

     This replaces a four-step scale (10 / 14 / 18 / 24) plus a scatter of one-off
     literals (5, 6, 7, 8, 20, 50%, 999px). Four values is the whole vocabulary
     now: if a new rule needs a corner, it is one of these, and `0` for a surface
     that deliberately goes full-bleed (the phone layout). Anything else is a bug.
     --r-sm is the newest and narrowest in scope -- reach for --r first, and only
     use --r-sm when a 24px arc is demonstrably cutting into content.
     -------------------------------------------------------------------------- */
  --r-sm: 16px;
  --r: 24px;
  --r-xl: 40px;
  --r-pill: 100px;
  --chat-font-size: 14.5px;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --hover: rgba(133,133,133,0.10);
  --hover-2: rgba(133,133,133,0.16);
  --tint: rgba(240,103,49,0.10);
  --elev-1: 0 1px 2px rgba(10,10,10,0.05), 0 4px 16px rgba(10,10,10,0.05);
  --elev-2: 0 8px 34px rgba(10,10,10,0.12);

  /* ---- responsive shell metrics -------------------------------------------
     These are the only knobs the breakpoint bands in chat-responsive.css turn.
     They are deliberately separate from the --sp-* spacing scale: --sp-4 is a
     fixed 16px step used for padding inside components, whereas these describe
     the app shell's own geometry and shrink to zero on a phone, where the
     card-in-card look gives way to an edge-to-edge layout.

       --shell-pad   gap between the shell edge and the floating cards
       --shell-gap   gap BETWEEN the sidebar/rail and the panel beside it
       --rail-w      width of the collapsed icon rail
       --reading-max the reading column's comfortable measure
       --reading-pad its minimum side padding on narrow viewports
       --tap         minimum hit target; grows on touch pointers
       --hero-*      empty-state hero type, fluid rather than fixed
       --safe-b      bottom safe-area inset (iOS home indicator)
     ------------------------------------------------------------------------ */
  --shell-pad: var(--sp-4);
  --shell-gap: var(--sp-4);
  --rail-w: 60px;
  --reading-max: 768px;
  --reading-pad: 22px;
  --lib-card-min: 232px;    /* Library grid's auto-fill minimum track */
  --tap: 34px;
  --hero-h2: 32px;
  --hero-sub: 14.5px;

  /* ---- composer resting height -------------------------------------------
     The composer pill's height with one line of text in it, and the height of anything
     meant to read as the same kind of control: both search fields (.search-field) and the
     Library's sort button, plus --composer-zone below.

     The reference product's search bar is 74px (a 52px circular button with 10px padding
     around it). That was tried and rejected as too large for this app -- inside a 274px
     sidebar-width panel it dominated the toolbar. The reference's other traits (white
     fill, 1px hairline, 100px radius, 32px text inset, black circular button) are kept;
     only the overall height comes back to the composer's.

     The composer itself does NOT consume this: its height is intrinsic, because the
     textarea autosizes as you type (js/chat/composer.js measures scrollHeight). So this
     DERIVES the composer's resting height instead of restating it: --pad-y twice plus one
     line box, which is --chat-font-size at the 1.5 line-height that css/chat-composer.css
     sets on `textarea, .ghost`.

     Derived rather than a literal 52px because both inputs move: the phone band retunes
     --pad-y to 13px, and Settings' Small/Medium/Large changes --chat-font-size. A hard
     52 was already 4px out from the composer below 479px, which is exactly the drift a
     shared token was supposed to prevent.

     --pad-y lives on :root (not on .field) so this calc can see it; .field inherits it.
     ---------------------------------------------------------------------- */
  --pad-y: 15px;
  --field-h: calc(var(--pad-y) * 2 + var(--chat-font-size) * 1.5);

  /* ---- composer keep-out strip -------------------------------------------
     How much vertical space the composer occupies at the bottom of the viewport,
     measured from the viewport's bottom edge upward. Centred overlays reserve it as
     padding so they cannot sit on top of the input (see .settings-modal).

     It is the <form>'s own height plus the shell padding beneath it: the form's 10px
     top pad, a single-line .field (--field-h), its 20px bottom pad, then --shell-pad.
     Built from --field-h rather than the old hardcoded 82px, so the field height lives
     in exactly one place. Deliberately keyed to the SINGLE-LINE height: a composer
     expanded to several lines while the Settings modal is also open is not a state
     worth shrinking the modal for.
     ---------------------------------------------------------------------- */
  --composer-zone: calc(10px + var(--field-h) + 20px + var(--shell-pad));

  /* ---- drawer/panel horizontal inset -------------------------------------
     The single left/right inset for a slide-over panel's header AND its body, so the
     panel title lines up with the content under it. They used to carry the number
     separately (head 18px, body 18px) and the ≤767px band then re-stated only the
     body at 14px, which left the "Library" heading 4px out from the search field and
     cards beneath it. One token, so a breakpoint can only move both together.

     The value is DERIVED FROM THE CORNER RADIUS rather than picked. A panel with a 40px
     radius curves away for the first 40px of each edge, so content inset only 18px sits
     inside that arc and reads as crowding the corner even though it is technically
     aligned -- which is exactly how the "Library" heading looked, 18px from the left and
     16px from the top of a 40px corner. 24px then 32px both still read as crowding it, so
     the inset is now the FULL radius: content starts exactly where the corner arc ends and
     the panel's edge becomes straight. That is the natural stopping point rather than
     another guess -- inside 40px of a 40px corner there is curve, outside it there is not
     -- and it is the standard safe-area rule for a rounded container.
     Written as the token itself, not a multiplier, so it tracks --r-xl exactly.

     The phone band overrides this with a flat 14px: there the panel is full-bleed with
     `border-radius: 0`, so there is no arc to clear and the inset is free to be tighter.
     ---------------------------------------------------------------------- */
  --drawer-pad-x: var(--r-xl);

  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);

  /* ---- solid brand-black button ------------------------------------------
     The AlphaVenture Primary palette is orange #F06731, black #0A0A0A and
     white #FFFFFF. This pair is the black-on-white half of it, used for the
     composer's send/stop control, the New chat pill, the tooltips, the search
     field's circular button and the Library sort button.

     Kept as a token rather than inlined now that there is only one theme (it used to
     invert for dark mode) because it names a ROLE -- "the solid brand-black button" --
     and five unrelated components read it. One declaration is what keeps them the same
     object; five literals would be five things that merely happen to match today.
     ----------------------------------------------------------------------- */
  --btn-solid-bg: #0A0A0A;
  --btn-solid-fg: #FFFFFF;
  --btn-solid-bg-hover: #292929;

  /* ---- scrollbars ---------------------------------------------------------
     Every scroll surface in the app draws its thumb from these two, so the
     conversation list, the Settings modal, the artifact drawer and the Library
     can never end up with differently coloured scrollbars.

     Both values are approved AlphaVenture neutrals, one step apart on the grey scale:
     #E0E0E0 at rest, #D1D1D1 while the pointer is over the thumb OR while it is being
     dragged. The dragged state matters as much as hover -- a thumb that pales the
     instant you grab it reads as unresponsive -- so ::-webkit-scrollbar-thumb:active
     takes the same value (see chat-messages.css).

     Both greys read against either surface level (#F2F2F2 and #FFFFFF) without ever
     competing with the content, which is what lets one pair serve every scroll surface.
     ---------------------------------------------------------------------- */
  --scroll-thumb: #E0E0E0;
  --scroll-thumb-hover: #D1D1D1;

  /* ---- surfaces: separated by contrast, not by strokes --------------------
     Every ring border in the app is gone (`box-shadow` was already globally
     disabled below), so a surface is told apart from the one under it purely by
     its fill. That only works if adjacent levels never share a value, hence an
     ALTERNATING scale keyed to nesting depth:

       --surface-0   the app shell, backmost            #F2F2F2
       --surface-1   a card sitting ON the shell         #FFFFFF
       --surface-2   an element sitting ON a card        #F2F2F2
       --surface-3   an element sitting ON one of those  #FFFFFF

     Choose the level by counting how deeply the element is stacked, not by what
     kind of thing it is. Two nested surfaces at the same level is the one bug
     this scale exists to prevent: with no border and no shadow, they merge.

     Only two distinct values are in play, alternating: #F2F2F2 and #FFFFFF, both
     approved AlphaVenture neutrals. Four tokens rather than two because the NAME is
     the depth, so a rule says which level it is at and cannot pick the wrong grey.
     -------------------------------------------------------------------- */
  --surface-0: #F2F2F2;
  --surface-1: #FFFFFF;
  --surface-2: #F2F2F2;
  --surface-3: #FFFFFF;

  /* Placeholder text stays grey while ordinary text is black. It is the one
     "grey text" that is doing real work: it marks a field as EMPTY, and at
     --text it would be indistinguishable from something the user had typed. */
  --placeholder: #858585;

  /* Colour for thin decorative LINES and RINGS: keyboard focus rings, the
     drag-to-resize hairlines. These used to read from --muted/--text, which was fine
     while --muted was a mid grey, but became a hard black hairline the moment "grey
     text goes black" was applied to it. Text going black is about legibility; a black
     ring around things is just a black ring around things. Kept as its own token so
     the two can never be conflated again. */
  --ring: #D1D1D1;

  /* ---- AlphaVenture star --------------------------------------------------
     The brand star, traced from the official asset (Star 1.svg), 234x226.

     Declared once, as a CSS mask, because it renders in two places that are
     built by different systems: the sidebar brand lockup is static markup in
     index.html, and the welcome hero is assembled in js/chat/message-row.js.
     Inlining the <svg> in both would put the same path data in two files that
     have no reason to stay in step with each other.

     A mask rather than an <img> or a background-image because a mask takes its
     colour from `background-color`, which is set to `currentColor` below: a
     caller sets `color` and the star follows, exactly like an icon font or an
     inline SVG with fill="currentColor" would. `contain` keeps the 234:226
     ratio whatever box it's given, so it can never be stretched.
     ---------------------------------------------------------------------- */
  /* The brand star, from the supplied artwork (frontend/img/av-star.png).
     It arrived as a 1444x1441 RGBA raster inside an .svg wrapper (141 KB) -- not a
     vector, so the previous inline path data could not simply be swapped. Processed
     rather than embedded as-is:
       - the shape is a single orange hue on transparency, so only the ALPHA channel is
         kept. That makes it a mask, exactly like the outgoing vector, which is what lets
         the star keep taking `currentColor` (orange in the hero, --text elsewhere).
       - sub-10% alpha is dropped as compression noise, then resampled to 160px: 2.1 KB
         instead of 141 KB, and it stays crisp at every size the app draws it (19-32px).
     A true vector would be smaller again and sharper still; if an .svg with real path
     data turns up, drop it in and this becomes a one-line change. */
  --star-mask: url("../img/av-star.png");
}
/* ---- ONE THEME ------------------------------------------------------------------
   This app is light-only. There is no dark mode, no `prefers-color-scheme` block, no
   `data-theme` attribute and no Appearance control in Settings -- the palette above is
   the palette, full stop.

   There used to be three more token blocks here: a `@media (prefers-color-scheme: dark)`
   override, a `:root[data-theme="light"]` block and a `:root[data-theme="dark"]` block.
   Deleting them changed nothing about how the app renders, because the light blocks only
   ever restated the values already declared on `:root` above and in report-theme.css.
   That duplication was the actual cost of the feature: the same colour lived in up to
   three places, and a token added to one block and forgotten in another produced a bug
   visible in only one mode.

   The remaining tokens this file USES but does not declare -- --bg, --panel, --border,
   --text, --muted, --accent, --accent-press, --link, --shadow and the two font stacks --
   come from css/report-theme.css, which is loaded first and is the single source for the
   palette shared with companies.html and company.html.

   --muted is deliberately the SAME value as --text. "Muted" text (labels, subtitles,
   metadata, timestamps, icon strokes) is full black rather than a grey step down. The
   token is kept rather than search-replaced away because it still marks which text is
   secondary BY ROLE, which is what a later change would need in order to reintroduce a
   tint in one place without hunting through every rule that happens to use black.
   ------------------------------------------------------------------------------- */
* { box-sizing: border-box; }
/* No outlines anywhere — strictly. */
* { outline: none !important; }
/* No drop shadows anywhere — surfaces are distinguished by alternating fills only.
   ::before/::after are listed explicitly because `*` matches ELEMENTS, not pseudo-
   elements: the hover tooltips (.tip::after) kept a `0 6px 18px rgba(10,10,10,0.28)`
   shadow for exactly that reason, which was visible poking out above the composer. */
*, *::before, *::after { box-shadow: none !important; }
/* Every ring border in the app was removed by giving up an explicit `border`
   declaration each rule used to carry, on the assumption that no border means no
   border. `<button>`, `<input>`, `<textarea>` and `<select>` don't work that way:
   without an author rule they fall back to the user agent's OWN border (a chip
   rendered as a plain HTML button, for instance, got its browser's native 2px outset
   button bevel back the moment its own `border` line was deleted). This is the reset
   that keeps "no author border" actually meaning no border, for every current use of
   these elements and any added later.

   Exactly two elements opt back IN to a border, and only these two in the whole
   chatbot: .settings-instructions and .about-link (see the "STROKE EXCEPTION" notes in
   chat-overlays.css). Both are white-on-white inside the Settings panel, so a 1px
   hairline is the only thing available to separate them. This rule is a plain element
   selector with no !important, so their class-level rules win on specificity -- which
   is the intended relationship, not an accident to be tightened later. */
button, input, textarea, select { border: none; }
/* overflow:hidden here matters: .app is a self-contained 100dvh shell with its
   own internal scroll areas (.messages, sidebar .list, etc.), the outer
   document should never need its own scrollbar. Without this, a stray 1px of
   overflow (a box-shadow, a rounding difference) can flash a native scrollbar
   at the very edge of the viewport, outside the rounded shell entirely. */
html, body { height: 100%; margin: 0; overflow: hidden; }
body {
  font-family: var(--font-body);
  background:
    radial-gradient(1200px 640px at 84% -16%, rgba(240,103,49,0.07), transparent 58%),
    radial-gradient(900px 520px at -12% 116%, rgba(243,133,90,0.05), transparent 55%),
    var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

/* ---- weight: this rule is the 500 tier, not the app's ceiling ---------------
   The app has three weights, chosen by ROLE (see CLAUDE.md, "Weight: three tiers"):
   400 body, 500 sub-headings and inline emphasis, 700 headings. This rule is the
   middle tier; 700 is set on the titling elements themselves (.drawer-title, the
   welcome hero) rather than here.

   `strong`/`b`/`th` are the reason the rule exists and they stay at 500. They come out
   of the markdown renderer for model-authored `**bold**`, so no rule of ours would
   otherwise apply and they would inherit the user agent's `font-weight: bolder` (700+)
   -- which would put emphasis at the same weight as a panel title. Scoped to the app's
   own text surfaces rather than declared globally, so a sandboxed report iframe keeps
   its own typography.

   h1-h6 are held at 500 here on purpose: within the app the only ones that appear are
   markdown headings inside a bot message, which are content rather than app chrome and
   should not outweigh the panel title above them. (They also carry their own explicit
   500 in chat-messages.css, so this is belt-and-braces.)
   ------------------------------------------------------------------------- */
strong, b, th, h1, h2, h3, h4, h5, h6, optgroup { font-weight: 500; }

/* Every placeholder in the app, stated once. Several fields (the composer, the
   sidebar search) previously just took the user agent's default grey, which happened
   to look right while --muted was also grey. Now that ordinary text is black, relying
   on a UA default would leave those placeholders at a colour nothing in this codebase
   controls. See --placeholder for why they stay grey at all. */
::placeholder { color: var(--placeholder); opacity: 1; }

/* ---- strokes: removed, everywhere ------------------------------------------
   No element gets a ring border. Surfaces are told apart by the alternating
   --surface-* fills instead (see the token block above).

   Two things this deliberately does NOT touch:
   - `:focus-visible` outlines. Those are not decoration, they are the only way a
     keyboard user can see where they are, and removing them is an accessibility
     regression rather than a style change.
   - Single-side hairline rules used as SEPARATORS between rows in one surface
     (settings rows, the sidebar header/footer, a drawer header). Those divide
     content inside a single fill, where there is no second fill available to do
     the job, so they stay.
   ------------------------------------------------------------------------- */
.av-star {
  display: inline-block; flex: none;
  aspect-ratio: 1;  /* the supplied artwork is square (1444x1441) */
  background-color: currentColor;
  -webkit-mask: var(--star-mask) center / contain no-repeat;
          mask: var(--star-mask) center / contain no-repeat;
}

