{"version":3,"file":"AIChat.module.cjs","names":[],"sources":["../../../src/components/AIChat/AIChat.module.css"],"sourcesContent":[".panel {\n    display: flex;\n    flex-direction: column;\n    min-width: 0;\n    min-height: 0;\n    height: var(--tempest-ai-chat-height, 100%);\n    background-color: var(--tempest-bg);\n    color: var(--tempest-text);\n    font-family: var(--tempest-font-sans);\n}\n\n.header {\n    flex: 0 0 auto;\n    padding: var(--tempest-space-3) var(--tempest-space-4);\n    border-bottom: 1px solid var(--tempest-border);\n}\n\n/*\n * The wrapper exists only so the jump-to-latest button can be positioned against\n * the scroll area without being scrolled away with it.\n */\n.threadWrapper {\n    position: relative;\n    display: flex;\n    flex: 1 1 auto;\n    min-width: 0;\n    min-height: 0;\n}\n\n/*\n * `min-width: 0` is load-bearing, not defensive.\n *\n * A flex item defaults to `min-width: auto`, which means \"at least as wide as my\n * content wants\". An answer carrying a wide table or a long line of SQL would make\n * the scroll area itself grow past the panel — and the table's own `overflow-x`\n * inside `Markdown` never gets a chance to kick in, because it is already as wide as\n * it asked to be. Zeroing the floor here is what pushes the overflow back down to\n * the table and the code block, where it scrolls in its own box.\n */\n.thread {\n    display: flex;\n    flex: 1 1 auto;\n    flex-direction: column;\n    gap: var(--tempest-space-5);\n    min-width: 0;\n    min-height: 0;\n    padding: var(--tempest-space-4);\n    overflow-y: auto;\n    overscroll-behavior: contain;\n}\n\n.thread:focus-visible {\n    outline: 2px solid var(--tempest-primary);\n    outline-offset: -2px;\n}\n\n.jump {\n    position: absolute;\n    right: var(--tempest-space-4);\n    bottom: var(--tempest-space-3);\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 2rem;\n    height: 2rem;\n    padding: 0;\n    border: 1px solid var(--tempest-border);\n    border-radius: var(--tempest-radius-full);\n    background-color: var(--tempest-surface);\n    color: var(--tempest-text);\n    box-shadow: var(--tempest-shadow-md);\n    cursor: pointer;\n}\n\n.jump:focus-visible {\n    outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n    outline-offset: 2px;\n}\n\n/* Same 44×44 hit-slop as the turn actions: 2rem is a small target for a thumb. */\n@media (pointer: coarse) {\n    .jump::after {\n        content: \"\";\n        position: absolute;\n        inset: -6px;\n    }\n}\n\n/* --- turn ------------------------------------------------------------------ */\n\n/*\n * Turns, the thinking row, the suggestions and the composer all share one reading\n * column — `--tempest-ai-chat-width`, 48rem by default — centred in whatever width\n * the panel got.\n *\n * A capped column is the right answer from a phone up to a 1920 desktop: prose past\n * roughly 90 characters per line is measurably harder to track back to the start of\n * the next line, and letting an answer run the full width of a wide monitor makes it\n * worse, not more useful. It is deliberately a **knob** rather than a constant\n * because at 2560 and above the trade flips — a 768px ribbon in the middle of a\n * living-room screen is mostly empty room, and only the app knows how far away its\n * user is sitting. Widen it at the root: `--tempest-ai-chat-width: 72rem`.\n *\n * Type size is **not** solved here. Scaling the font for a TV is a decision for\n * `typography.css` and `density.css` — a component-local font ramp would fight the\n * tokens every app themes through.\n */\n.turn {\n    display: flex;\n    flex-direction: column;\n    gap: var(--tempest-space-2);\n    min-width: 0;\n    max-width: var(--tempest-ai-chat-width, 48rem);\n    width: 100%;\n    margin-inline: auto;\n}\n\n/*\n * An assistant turn is full width with no bubble, a user turn is a narrow bubble\n * pushed to the end of the row.\n *\n * That asymmetry is the whole reading model of an LLM transcript: the answer is the\n * document being read, and wrapping it in a bubble would cap its width, fight the\n * code blocks and tables inside it, and make a long answer look like a shouted\n * message. The prompt is short and needs to be told apart at a glance, which a\n * bubble does better than anything else.\n */\n.userTurn {\n    align-items: flex-end;\n}\n\n.systemTurn {\n    padding: var(--tempest-space-2) var(--tempest-space-3);\n    border: 1px dashed var(--tempest-border);\n    border-radius: var(--tempest-radius-md);\n    background-color: var(--tempest-surface);\n    color: var(--tempest-text-muted);\n    font-size: var(--tempest-text-sm);\n}\n\n.turnHeader {\n    display: flex;\n    align-items: center;\n    gap: var(--tempest-space-2);\n    min-width: 0;\n}\n\n.avatar {\n    display: inline-flex;\n    flex: 0 0 auto;\n}\n\n.role {\n    color: var(--tempest-text-muted);\n    font-size: var(--tempest-text-xs);\n    font-weight: var(--tempest-weight-semibold);\n}\n\n/*\n * The role of a user turn is announced but not drawn: the bubble on the other side\n * already says who wrote it, and a \"Você\" label over every prompt is noise. Screen\n * readers still get it, because side and colour are not information for them.\n */\n.visuallyHiddenRole {\n    position: absolute;\n    width: 1px;\n    height: 1px;\n    margin: -1px;\n    padding: 0;\n    overflow: hidden;\n    border: 0;\n    clip-path: inset(50%);\n    white-space: nowrap;\n}\n\n/*\n * The meta row uses the muted token, not the subtle one.\n *\n * `--tempest-text-subtle` at `--tempest-text-2xs` measures **4.37:1** against\n * `--tempest-bg` in the dark theme — under the 4.5:1 floor for text. Measured in a\n * real browser; jsdom's `axe` turns `color-contrast` off because nothing is painted.\n * Size is what de-emphasises this row; colour still has to pass.\n */\n.model,\n.time {\n    color: var(--tempest-text-muted);\n    font-size: var(--tempest-text-2xs);\n    font-variant-numeric: tabular-nums;\n}\n\n.body {\n    min-width: 0;\n    overflow-wrap: anywhere;\n}\n\n.userTurn .body {\n    padding: var(--tempest-space-2) var(--tempest-space-3);\n    border-radius: var(--tempest-radius-lg);\n    background-color: var(--tempest-primary-soft);\n    color: var(--tempest-primary-on-soft);\n    font-size: var(--tempest-text-sm);\n    line-height: var(--tempest-leading-normal);\n    max-width: min(85%, 36rem);\n}\n\n.plain {\n    white-space: pre-wrap;\n}\n\n.markdownRoot {\n    font-size: var(--tempest-text-sm);\n    line-height: var(--tempest-leading-relaxed);\n}\n\n/*\n * The caret rides the last block of the answer, so it sits at the end of the text\n * instead of on a line of its own.\n *\n * `::after` on the final child is inline content, which is exactly the behaviour\n * wanted: it flows after the last word and moves with it as more text arrives. When\n * the answer starts with a fenced block the caret lands inside the code area — which\n * is where the text is actually being appended, so it is still telling the truth.\n */\n.streamingBody > .markdownRoot > :last-child::after {\n    content: \"▍\";\n    margin-left: 0.125em;\n    color: var(--tempest-primary);\n    animation: caretBlink 1s steps(2, jump-none) infinite;\n}\n\n@keyframes caretBlink {\n    to {\n        opacity: 0.2;\n    }\n}\n\n@media (prefers-reduced-motion: reduce) {\n    .streamingBody > .markdownRoot > :last-child::after {\n        animation: none;\n    }\n}\n\n.dots {\n    display: inline-flex;\n    align-items: center;\n    gap: 3px;\n}\n\n.dots > span {\n    width: 5px;\n    height: 5px;\n    border-radius: 50%;\n    background-color: var(--tempest-text-subtle);\n    animation: dotPulse 1.2s var(--tempest-ease-in-out) infinite;\n}\n\n.dots > span:nth-child(2) {\n    animation-delay: 0.15s;\n}\n\n.dots > span:nth-child(3) {\n    animation-delay: 0.3s;\n}\n\n@keyframes dotPulse {\n    0%,\n    60%,\n    100% {\n        opacity: 0.25;\n    }\n\n    30% {\n        opacity: 1;\n    }\n}\n\n@media (prefers-reduced-motion: reduce) {\n    .dots > span {\n        animation: none;\n        opacity: 0.6;\n    }\n}\n\n.thinking {\n    display: flex;\n    align-items: center;\n    gap: var(--tempest-space-2);\n    max-width: var(--tempest-ai-chat-width, 48rem);\n    width: 100%;\n    margin: 0 auto;\n    color: var(--tempest-text-muted);\n    font-size: var(--tempest-text-sm);\n}\n\n/* --- reasoning ------------------------------------------------------------- */\n\n.reasoning {\n    border: 1px solid var(--tempest-border);\n    border-radius: var(--tempest-radius-md);\n    background-color: var(--tempest-surface);\n}\n\n/*\n * Class plus attribute, so this beats `Collapsible`'s own single-class `.trigger`\n * rule whatever order the two modules land in the built stylesheet.\n */\n.reasoning button[aria-expanded] {\n    color: var(--tempest-text-muted);\n    font-size: var(--tempest-text-xs);\n    font-weight: var(--tempest-weight-medium);\n}\n\n.reasoning button[aria-expanded=\"true\"] .reasoningChevron {\n    transform: rotate(90deg);\n}\n\n.reasoningChevron {\n    flex: 0 0 auto;\n    transition: transform var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n    .reasoningChevron {\n        transition: none;\n    }\n}\n\n.reasoningBody {\n    color: var(--tempest-text-muted);\n    font-size: var(--tempest-text-xs);\n    line-height: var(--tempest-leading-relaxed);\n    white-space: pre-wrap;\n}\n\n/* --- attachments ----------------------------------------------------------- */\n\n.attachments {\n    display: flex;\n    flex-wrap: wrap;\n    gap: var(--tempest-space-2);\n    margin: 0;\n    padding: 0;\n    list-style: none;\n}\n\n.attachment {\n    display: inline-flex;\n    align-items: center;\n    gap: var(--tempest-space-2);\n    max-width: 16rem;\n    padding: var(--tempest-space-1) var(--tempest-space-2);\n    border: 1px solid var(--tempest-border);\n    border-radius: var(--tempest-radius-md);\n    background-color: var(--tempest-surface);\n    font-size: var(--tempest-text-xs);\n}\n\n.attachmentName {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n}\n\n.attachmentMeta {\n    flex: 0 0 auto;\n    color: var(--tempest-text-muted);\n    font-size: var(--tempest-text-xs);\n}\n\n.thumb {\n    display: block;\n    width: 4rem;\n    height: 4rem;\n    border-radius: var(--tempest-radius-sm);\n    object-fit: cover;\n}\n\n/* --- error ----------------------------------------------------------------- */\n\n.error {\n    display: flex;\n    align-items: center;\n    gap: var(--tempest-space-2);\n    margin: 0;\n    padding: var(--tempest-space-2) var(--tempest-space-3);\n    border: 1px solid var(--tempest-danger);\n    border-radius: var(--tempest-radius-md);\n    background-color: var(--tempest-danger-bg);\n    color: var(--tempest-text);\n    font-size: var(--tempest-text-sm);\n}\n\n.retry {\n    padding: 0;\n    border: none;\n    background: none;\n    color: var(--tempest-danger);\n    font: inherit;\n    font-weight: var(--tempest-weight-medium);\n    text-decoration: underline;\n    cursor: pointer;\n}\n\n/* --- actions --------------------------------------------------------------- */\n\n.actions {\n    display: flex;\n    align-items: center;\n    gap: var(--tempest-space-1);\n    opacity: 0;\n    transition: opacity var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n/*\n * The row is revealed on hover and on keyboard focus, and is **always** visible\n * where there is no hover at all.\n *\n * Hiding a control behind `:hover` on a touch screen hides it for good — there is no\n * hover event to trigger it, and the first tap would land on whatever is underneath.\n */\n.turn:hover .actions,\n.actions:focus-within {\n    opacity: 1;\n}\n\n@media (hover: none) {\n    .actions {\n        opacity: 1;\n    }\n}\n\n@media (prefers-reduced-motion: reduce) {\n    .actions {\n        transition: none;\n    }\n}\n\n/*\n * Two classes so the ghost styling wins over `CopyButton`'s own `.button` rule\n * regardless of the order the two CSS modules land in `styles.css`.\n */\n.actions .action {\n    position: relative;\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 1.75rem;\n    height: 1.75rem;\n    padding: 0;\n    border: none;\n    border-radius: var(--tempest-radius-sm);\n    background: none;\n    color: var(--tempest-text-muted);\n    cursor: pointer;\n    transition: var(--tempest-transition-color);\n}\n\n/*\n * Touch hit-slop — grows the tap target to 44×44 on a coarse pointer without moving\n * a pixel of the visible button, the same trick `Button` uses for its icon-only\n * sizes.\n *\n * 28×28 clears the 24×24 floor of WCAG 2.5.8 but sits well under the 44×44 of 2.5.5,\n * and four of these sit side by side: on a phone, \"copy\" and \"thumbs down\" are two\n * thumb-widths apart. Padding the button instead would space the row out on desktop,\n * where the pointer is precise and the row should stay quiet.\n */\n@media (pointer: coarse) {\n    .actions .action::after {\n        content: \"\";\n        position: absolute;\n        inset: -8px;\n    }\n}\n\n@media (hover: hover) and (pointer: fine) {\n    .actions .action:hover {\n        background-color: var(--tempest-surface-2);\n        color: var(--tempest-text);\n    }\n}\n\n.actions .action:focus-visible {\n    outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n    outline-offset: 1px;\n}\n\n.actions .action[data-copied],\n.actions .actionActive {\n    background: none;\n    color: var(--tempest-primary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n    .actions .action {\n        transition: none;\n    }\n}\n\n/* --- edit ------------------------------------------------------------------ */\n\n.editor {\n    display: flex;\n    flex-direction: column;\n    gap: var(--tempest-space-2);\n    width: 100%;\n    max-width: min(85%, 36rem);\n}\n\n.editField {\n    width: 100%;\n    padding: var(--tempest-space-2) var(--tempest-space-3);\n    border: 1px solid var(--tempest-primary);\n    border-radius: var(--tempest-radius-lg);\n    background-color: var(--tempest-bg);\n    color: var(--tempest-text);\n    font: inherit;\n    font-size: var(--tempest-text-sm);\n    line-height: var(--tempest-leading-normal);\n    resize: vertical;\n}\n\n.editField:focus-visible {\n    outline: 2px solid var(--tempest-primary);\n    outline-offset: -1px;\n}\n\n.editActions {\n    display: flex;\n    justify-content: flex-end;\n    gap: var(--tempest-space-2);\n}\n\n.ghost {\n    padding: var(--tempest-space-1) var(--tempest-space-3);\n    border: 1px solid var(--tempest-border);\n    border-radius: var(--tempest-radius-md);\n    background-color: transparent;\n    color: var(--tempest-text);\n    font: inherit;\n    font-size: var(--tempest-text-sm);\n    cursor: pointer;\n}\n\n.primary {\n    padding: var(--tempest-space-1) var(--tempest-space-3);\n    border: none;\n    border-radius: var(--tempest-radius-md);\n    background-color: var(--tempest-primary);\n    color: var(--tempest-primary-foreground);\n    font: inherit;\n    font-size: var(--tempest-text-sm);\n    font-weight: var(--tempest-weight-medium);\n    cursor: pointer;\n}\n\n.primary:disabled {\n    background-color: var(--tempest-surface-3);\n    color: var(--tempest-text-subtle);\n    cursor: not-allowed;\n}\n\n/* --- suggestions ----------------------------------------------------------- */\n\n.suggestions {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n    gap: var(--tempest-space-2);\n    max-width: var(--tempest-ai-chat-width, 48rem);\n    width: 100%;\n    margin: auto auto 0;\n}\n\n.suggestionsTitle {\n    margin: 0;\n    color: var(--tempest-text-muted);\n    font-size: var(--tempest-text-sm);\n}\n\n.suggestion {\n    padding: var(--tempest-space-2) var(--tempest-space-3);\n    border: 1px solid var(--tempest-border);\n    border-radius: var(--tempest-radius-md);\n    background-color: var(--tempest-surface);\n    color: var(--tempest-text);\n    font: inherit;\n    font-size: var(--tempest-text-sm);\n    text-align: left;\n    cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n    .suggestion:hover {\n        background-color: var(--tempest-surface-2);\n    }\n}\n\n.suggestion:focus-visible {\n    outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n    outline-offset: 2px;\n}\n\n/* --- composer -------------------------------------------------------------- */\n\n.composer {\n    display: flex;\n    flex: 0 0 auto;\n    flex-direction: column;\n    gap: var(--tempest-space-1);\n    padding: var(--tempest-space-3) var(--tempest-space-4);\n    border-top: 1px solid var(--tempest-border);\n}\n\n/*\n * The field and the buttons share one bordered box, and the border is drawn here\n * rather than on the textarea: that is what makes the attach control and the send\n * button read as part of the prompt instead of a toolbar next to it.\n */\n.composerBox {\n    display: flex;\n    align-items: flex-end;\n    gap: var(--tempest-space-2);\n    max-width: var(--tempest-ai-chat-width, 48rem);\n    width: 100%;\n    margin-inline: auto;\n    padding: var(--tempest-space-2);\n    border: 1px solid var(--tempest-border);\n    border-radius: var(--tempest-radius-lg);\n    background-color: var(--tempest-surface);\n}\n\n.composerBox:focus-within {\n    border-color: var(--tempest-primary);\n}\n\n.field {\n    flex: 1 1 auto;\n    min-width: 0;\n    padding: var(--tempest-space-1) var(--tempest-space-2);\n    border: none;\n    background: none;\n    color: inherit;\n    font: inherit;\n    font-size: var(--tempest-text-sm);\n    line-height: var(--tempest-leading-normal);\n    resize: none;\n}\n\n.field:focus-visible {\n    outline: none;\n}\n\n.field:disabled {\n    color: var(--tempest-text-subtle);\n    cursor: not-allowed;\n}\n\n.composerActions {\n    display: flex;\n    flex: 0 0 auto;\n    align-items: center;\n    gap: var(--tempest-space-2);\n}\n\n.composerFooter {\n    max-width: var(--tempest-ai-chat-width, 48rem);\n    width: 100%;\n    margin-inline: auto;\n    padding-inline: var(--tempest-space-2);\n    color: var(--tempest-text-muted);\n    font-size: var(--tempest-text-xs);\n}\n\n.send {\n    padding: var(--tempest-space-2) var(--tempest-space-4);\n    border: none;\n    border-radius: var(--tempest-radius-md);\n    background-color: var(--tempest-primary);\n    color: var(--tempest-primary-foreground);\n    font: inherit;\n    font-size: var(--tempest-text-sm);\n    font-weight: var(--tempest-weight-medium);\n    cursor: pointer;\n}\n\n.send:disabled {\n    background-color: var(--tempest-surface-3);\n    color: var(--tempest-text-subtle);\n    cursor: not-allowed;\n}\n\n/*\n * Stop replaces send in the same slot, so the one button under the thumb is always\n * the one that does the thing you want next — send while idle, abort while the answer\n * is coming. Two buttons side by side would mean aiming at the right one mid-stream.\n */\n.stop {\n    display: inline-flex;\n    align-items: center;\n    gap: var(--tempest-space-2);\n    padding: var(--tempest-space-2) var(--tempest-space-4);\n    border: 1px solid var(--tempest-border);\n    border-radius: var(--tempest-radius-md);\n    background-color: var(--tempest-surface-2);\n    color: var(--tempest-text);\n    font: inherit;\n    font-size: var(--tempest-text-sm);\n    font-weight: var(--tempest-weight-medium);\n    cursor: pointer;\n}\n\n.stopGlyph {\n    display: inline-block;\n    width: 0.625rem;\n    height: 0.625rem;\n    border-radius: 2px;\n    background-color: currentColor;\n}\n\n@media (max-width: 480px) {\n    .thread {\n        gap: var(--tempest-space-4);\n        padding: var(--tempest-space-3);\n    }\n\n    .userTurn .body,\n    .editor {\n        max-width: 100%;\n    }\n\n    .composer {\n        padding: var(--tempest-space-2) var(--tempest-space-3);\n    }\n}\n"],"mappings":";"}