  :root { color-scheme: light dark; }
  /* Light mode (the default). Forces a light color-scheme (so system colors stay light even on
     a dark OS) and a very-light-gray page background. Toggle in Settings; class is on <html>,
     set early by the head script below to avoid a flash. Off => follow the OS (light dark). */
  html.light { color-scheme: light; background: #f0f0f0; }
  /* max-width = the original 720px content width + the #frame's 3em of horizontal padding, so the
     conversation inside the frame keeps its previous width (and aspect ratio). */
  /* Centered both ways: max-width + margin:auto centers horizontally; a viewport-tall min-height
     plus flex justify-content:center centers the framed panel vertically in the window. The
     min-height subtracts the 2rem top+bottom margin so a short page fills exactly one viewport (no
     scrollbar); a tall page just grows past it and scrolls normally. */
  body { font: 15px/1.5 system-ui, sans-serif; max-width: calc(720px + 3em); margin: 2rem auto; padding: 0 1rem;
         min-height: calc(100vh - 4rem); display: flex; flex-direction: column; justify-content: center; }
  /* Fine-grained "grain field" watermark behind the centered column — dresses up
     the wide empty side margins. The asset (client/fg-grains-bg.svg) is full
     strength; the 25% display level lives HERE so it's easy to tune without
     regenerating. Fixed + z-index:-1 places it over the page background but
     behind all content; pointer-events:none keeps it inert. */
  body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background: url("/client/fg-grains-bg.svg") center center / cover no-repeat;
    opacity: .25;
    pointer-events: none;
  }
  button { font: inherit; padding: .4rem .8rem; border-radius: .5rem; border: 1px solid #8884; cursor: pointer; background: #8881; }
  button[aria-pressed="true"] { background: #2a7; color: #fff; border-color: #2a7; }
  button:disabled { opacity: .5; cursor: default; }
  #status { font-size: .85rem; opacity: .7; font-weight: normal; }

  /* Settings panel: OVERLAYS the conversation window (#convo) from its right edge, off-canvas
     until .open. Downscaled from the old full-height viewport slab; absolute so it floats over
     the chat rather than the whole page. (#convo is position:relative + overflow:hidden, so the
     closed panel is clipped just off its right edge.) */
  #settings { position: absolute; top: 0; right: 0; height: 100%; width: 13.5rem; max-width: 80%;
              box-sizing: border-box; padding: .7rem .8rem; background: Canvas; color: CanvasText;
              border-left: 1px solid #8886;
              transform: translateX(100%); transition: transform .22s ease; z-index: 12;
              display: flex; flex-direction: column; gap: .45rem; overflow-y: auto; font-size: .9rem; }
  #settings.open { transform: translateX(0); }
  #settingsBackdrop { position: absolute; inset: 0; background: rgba(0,0,0,.25); opacity: 0;
              pointer-events: none; transition: opacity .22s ease; z-index: 11; border-radius: .5rem; }
  #settingsBackdrop.open { opacity: 1; pointer-events: auto; }
  #settings .head { display: flex; align-items: center; justify-content: space-between; }
  #settings .head strong { font-size: .95rem; }
  #settingsClose { padding: .15rem .5rem; }
  #settings .srow { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
  /* Separate toggle groups: an extra 1.35rem on top of the panel's .45rem gap = 1.8rem above
     each group's first row (4x the normal inter-row spacing). */
  #settings .srow.group-top { margin-top: 1.35rem; }
  #settings hr { width: 100%; border: none; border-top: 1px solid #8884; margin: .25rem 0; }
  #settings .action { width: 100%; text-align: left; font-size: .85rem; padding: .35rem .6rem; }
  /* Toggle switches replace the old big On/Off buttons. The button IS the track; aria-pressed
     (set by the existing JS) slides the knob + flips the color. The On/Off text stays inside as
     an accessible label, just visually hidden. */
  #settings .srow button { position: relative; flex: none; width: 2.6rem; min-width: 2.6rem;
              height: 1.45rem; padding: 0; border-radius: 1rem; background: #8883; border: 1px solid #8886;
              transition: background .15s ease, border-color .15s ease; }
  #settings .srow button[aria-pressed="true"] { background: #2a7; border-color: #2a7; }
  #settings .srow button::after { content: ""; position: absolute; top: 50%; left: .15rem;
              transform: translateY(-50%); width: 1.05rem; height: 1.05rem; border-radius: 50%;
              background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.35); transition: left .15s ease; }
  #settings .srow button[aria-pressed="true"]::after { left: calc(100% - 1.05rem - .15rem); }
  #settings .srow button span { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
              overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
  /* Transcript as a chat: a flex column of bubbles. Alignment + tint carry the speaker
     (you = right, bot = left), so the turns drop the "you:"/"bot:" prefixes. */
  /* Extra top padding clears the floating corner controls (gear + Start/Pause): early messages
     start BELOW them, and as the chat overflows and auto-scrolls to the bottom, this padding
     scrolls out of view so older messages slide up under the controls. */
  #log { position: relative; background: #faf9f5; border: 1px solid #8884; border-radius: .5rem; padding: 4rem .75rem 1.75rem; height: 50vh; overflow-y: auto; white-space: pre-wrap; display: flex; flex-direction: column; gap: .8rem; }
  /* Dark-mode-aware chat background: off-white in light mode, mid-gray when following a dark OS
     (html.light forces light, so dark only applies when Light mode is toggled off). */
  @media (prefers-color-scheme: dark) { html:not(.light) #log { background: #4d4d4d; } }
  /* Positioning context for the floating corner controls (gear + Start/Pause). It wraps ONLY #log,
     so those buttons stay anchored to the chat box. (The gate scrim is positioned on #frame, not
     here, so it can cover the whole panel — see #startOverlay.) */
  #chat { position: relative; }
  /* Bubbles fill from the BOTTOM: auto top-margin on the first bubble pushes the whole column
     down while the chat is short, then collapses to 0 once it overflows so older turns scroll
     up under the corner controls. (Avoids the justify-content:flex-end overflow-clipping bug.) */
  #log > :first-child { margin-top: auto; }
  #log .you, #log .bot { max-width: 75%; padding: .4rem .65rem; border-radius: 1rem; }
  #log .you { align-self: flex-end; background: #28c6; border-bottom-right-radius: .25rem; }
  #log .bot { align-self: flex-start; background: #2a76; border-bottom-left-radius: .25rem; }
  /* In-transcript system notes (paused/resumed/concluded) — centered & muted so they read
     as events in the conversation, not as a speaker's turn. */
  #log .event { align-self: center; max-width: 85%; opacity: .65; font-style: italic; text-align: center; font-size: .85rem; margin: .15rem 0; }
  /* Inline "download transcript" chip dropped at the end of the stream on completion. */
  #log .dlchip { align-self: center; margin: .35rem 0 .1rem; font-size: .85rem; padding: .3rem .7rem; }

  /* Multiple-choice options, rendered in the thread under the bot's question. Centered; two
     options sit side by side, three or more stack. Selecting one disables the rest, tints the
     chosen button, and collapses the whole group (.collapsing) before transcriptUi removes it. */
  /* Full-thread width so the column variant has a definite width to center within — a shrink-to-fit
     box with width:100% children collapses to zero. Buttons are centered by the flex alignment. */
  /* flex-shrink:0 — #log is a flex column, and overflow:hidden (for the collapse) gives the item an
     automatic min-height of 0, so without this the constrained column shrinks the buttons to 0 height. */
  #log .choices { width: 100%; box-sizing: border-box; flex: 0 0 auto; display: flex;
                  justify-content: center; align-items: center; gap: .5rem; margin: .4rem 0 .25rem;
                  overflow: hidden; max-height: 30rem;
                  transition: max-height .3s ease, opacity .3s ease, margin .3s ease; }
  #log .choices.two { flex-direction: row; flex-wrap: wrap; }
  #log .choices.many { flex-direction: column; }
  #log .choices.collapsing { max-height: 0; opacity: 0; margin: 0; }
  #log .choice { font: inherit; cursor: pointer; padding: .45rem .9rem; border-radius: 1rem;
                 border: 1px solid #28c; background: #28c3; color: inherit;
                 transition: background .12s ease, transform .1s ease, opacity .12s ease; }
  #log .choices.many .choice { width: 100%; max-width: 18rem; text-align: center; }
  #log .choice:hover:not(:disabled) { background: #28c6; }
  #log .choice:active:not(:disabled) { transform: scale(.97); }
  #log .choice.chosen { background: #28c; color: #fff; border-color: #28c; }
  #log .choice:disabled { cursor: default; }
  #log .choice:disabled:not(.chosen) { opacity: .4; }

  /* The user composes their turn in an in-thread bubble (#composer), not a separate input row.
     #msg is a <textarea> styled as a right-aligned .you bubble; #send floats beneath it. The
     composer is hidden between turns (voice mode) and revealed during a dictated/typed turn
     (renderComposerVisibility); in text mode (capture off) it stays visible so you can type. */
  /* The composer hugs its text (autoGrowMsg) up to the same 75% ceiling as a committed bubble, so a
     long transcription spreads horizontally rather than wrapping into a cramped narrow column. */
  #composer { align-self: flex-end; display: flex; flex-direction: column; align-items: flex-end;
              max-width: 75%; margin-top: .15rem; }
  #composer.hidden { display: none; }
  /* The textarea-as-bubble: same tint/shape as a .you bubble, no input chrome. It auto-sizes to
     its content (autoGrowMsg). The mic-amplitude glow (userGlowTick) sets an inline box-shadow ring
     while speaking; the edit focus ring is the #composer.editing rule below. */
  #msg { box-sizing: border-box; resize: none; line-height: 1.4; min-height: 1.6rem; max-height: 40vh;
         overflow-y: hidden; width: 100%; min-width: 2.4rem; white-space: pre-wrap;
         padding: .4rem; border: 1px solid transparent; border-radius: 1rem;
         border-bottom-right-radius: .25rem; background: #28c6; color: inherit; font: inherit;
         transition: background .08s linear, box-shadow .08s linear, border-color .08s linear; }
  #msg::placeholder { font-style: italic; opacity: .7; }
  /* While dictating, the bubble is display-only (readOnly) — no caret/edit affordance. Once the
     user takes control (clicks "Undo send" or the bubble itself) it becomes editable with one
     radius-matched blue ring; suppress the browser's square focus outline either way. */
  #msg:read-only { cursor: default; }
  #msg:focus { outline: none; }
  #composer.editing #msg { box-shadow: 0 0 0 1px #28c; }
  /* Thin collapsible status bar above the transcript: one line when collapsed,
     expands downward into a small scroller of sys/diagnostic lines. */
  /* Hidden by default; the Settings "System status" toggle reveals it (body.show-sys). */
  #syspanel { border: 1px solid #8884; border-radius: .5rem; display: none; }
  body.show-sys #syspanel { display: block; }
  #syspanel > summary { cursor: pointer; padding: .4rem .7rem; list-style: none; display: flex; align-items: center; gap: .4rem; }
  #syspanel > summary::-webkit-details-marker { display: none; }
  #syspanel > summary::before { content: "▸"; opacity: .5; font-size: .7rem; }
  #syspanel[open] > summary::before { content: "▾"; }
  #sys { max-height: 28vh; overflow-y: auto; white-space: pre-wrap; padding: .25rem .7rem .5rem; border-top: 1px solid #8884; }
  #sys div { font-size: .78rem; opacity: .6; font-style: italic; padding: .1rem 0; }
  /* Send button: small + sleek, floating beneath the composer bubble (right-aligned). A fill
     sweeps across its width as the auto-send countdown runs; min-width keeps it from resizing
     when the label flips between "Send" and "Undo send". margin-top is its gap from the bubble. */
  #send { position: relative; overflow: hidden; min-width: 5.4rem; max-height: 3rem; margin-top: .35rem;
          font-size: .82rem; padding: .26rem .55rem; border-radius: .45rem;
          transition: opacity .15s ease, transform .15s ease, max-height .15s ease, margin-top .15s ease; }
  /* Bubble UX: the send control is hidden during dictation and "pops" in once the draft settles
     (see renderSendVisibility). .tucked fully collapses it (height + gap) so it leaves no blank
     space under the bubble, then pops/grows in. */
  #send.tucked { opacity: 0; transform: scale(.9); pointer-events: none;
                 max-height: 0; margin-top: 0; padding-top: 0; padding-bottom: 0; }
  #sendFill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: rgba(40,136,204,.5); pointer-events: none; }
  #sendLabel { position: relative; z-index: 1; }
  /* Text-only mode (Capture audio off): Send becomes the primary blue action. */
  #send.textmode { background: #28c; color: #fff; border-color: #28c; }

  /* Start gate. The dim scrim covers the whole #convo (see #startOverlay); the #start button is
     positioned within #chat. While .gated (startup + paused) the conversation is faded behind a
     large centered Start/Resume button; otherwise the same button shrinks to the top-right corner
     and floats over the chat. The center<->corner morph is animated in JS (FLIP), so layout here
     uses margins (not transforms) to avoid clobbering the inline transform. */
  /* Decorative mid-gray frame around the whole conversation window. Same corner radius as the
     chat box; generous padding so the chat sits inset within the gray surround. */
  #frame { position: relative; background: #d7d7d7; border-radius: .5rem; padding: 1.5em; }
  /* overflow:hidden keeps the off-canvas Settings drawer hidden. No vertical padding here, so the
     gray frame's gap stays symmetric top/bottom with the sides (the composer's mic-glow lives
     inside #log, which reserves its own room, so #convo needs none). */
  #convo { position: relative; overflow: hidden; }
  /* Scrim tint = the midpoint of the two bubble colors: you #28c (34,136,204) + bot #2a7
     (34,170,119) averaged → rgb(34,153,162). It's a child of #frame (its positioning context), so
     inset:0 covers the ENTIRE framed panel — the gray surround included — not just the chat box. Its
     corner radius matches #frame so the rounded corners stay clean. */
  #startOverlay { position: absolute; inset: 0; z-index: 5; border-radius: .5rem; background: rgb(34, 153, 162);
                  opacity: 0; pointer-events: none; transition: opacity .25s ease; }
  #convo.gated ~ #startOverlay { opacity: .6; pointer-events: auto; }
  /* Settings menu button: floats in the corner next to #start. The icon is a CSS-drawn
     hamburger (three bars) rather than a glyph, so it centers deterministically regardless of
     font metrics. Explicit height keeps it level with the corner Pause button. */
  #gear { position: absolute; z-index: 6; top: .55rem; right: .55rem; margin: 0;
          display: inline-flex; align-items: center; justify-content: center;
          box-sizing: border-box; height: 1.85rem; min-width: 1.95rem; padding: 0 .5rem;
          background: #d7d7d7; }
  /* .ham is the MIDDLE bar (flex-centered); its ::before/::after are the top/bottom bars,
     offset symmetrically — so the whole icon is centered no matter the button height. */
  #gear .ham, #gear .ham::before, #gear .ham::after {
          width: 1.1rem; height: 2px; background: currentColor; border-radius: 2px; }
  #gear .ham { position: relative; display: block; }
  #gear .ham::before, #gear .ham::after { content: ""; position: absolute; left: 0; }
  #gear .ham::before { top: -.34rem; }
  #gear .ham::after  { top:  .34rem; }
  #start { position: absolute; z-index: 6; top: .55rem; right: 3.5rem; left: auto; bottom: auto;
           margin: 0; font-size: .82rem; padding: .3rem .7rem; }
  /* Centered Start/Resume gate button: solid (opaque) background so the teal scrim behind it
     doesn't show through. */
  #convo.gated #start { top: 0; right: 0; bottom: 0; left: 0; margin: auto; width: max-content;
                        height: max-content; font-size: 1.15rem; padding: .7rem 1.4rem;
                        background: Canvas; box-shadow: 0 2px 10px rgba(0, 0, 0, .25); }
  /* Initial Start screen only (gate-initial, not a paused resume): lift the button up to make
     room for the instruction card below it. */
  #convo.gate-initial #start { top: 33%; bottom: auto; }
  #startHint { position: absolute; z-index: 6; left: 50%; top: 47%; transform: translateX(-50%);
               width: min(30rem, 84%); display: none; pointer-events: none; text-align: center;
               font-size: .9rem; line-height: 1.45; padding: .8rem 1rem; border-radius: .6rem;
               background: Canvas; border: 1px solid #8884; box-shadow: 0 2px 10px rgba(0, 0, 0, .2); }
  #convo.gate-initial #startHint { display: block; }
  /* Language toggle on the initial Start screen (mirrors the Settings "Portuguese mode"
     switch; lets the user pick Portuguese before pressing Start). Only shown on the very
     first gate, below the instruction card. */
  #startLang { position: absolute; z-index: 6; left: 50%; top: 22%; transform: translateX(-50%);
               display: none; align-items: center; gap: .45rem; cursor: pointer;
               font-size: .9rem; padding: .45rem .95rem; border-radius: 999px;
               background: Canvas; border: 1px solid #8884; box-shadow: 0 2px 10px rgba(0, 0, 0, .15); }
  #convo.gate-initial #startLang { display: inline-flex; }
  /* color: CanvasText keeps the label legible — without it the global button[aria-pressed]
     rule paints the text #fff, which is invisible on this light tinted pill. */
  #startLang[aria-pressed="true"] { border-color: #2a7; background: color-mix(in srgb, #2a7 12%, Canvas); color: CanvasText; }
