/* ============================================================================
   FEEDBACK

   A topbar icon, the panel it opens, and the reminder that arrives from
   it. The icon lives in the page chrome (feedback/trigger.html, rendered
   once for the navbar and once for the mobile logo header); everything
   it opens lives in a fixed overlay that feedback.js parks under
   whichever of the two is on screen.

   The overlay is a zero-sized point at that anchor — the trigger's
   horizontal centre, just below it — and the panel, the nudge and the
   right-click menu are placed against it. `--feedback-tail-x` is the
   distance from a surface's left edge back to that centre, so the
   nudge's tail keeps pointing at the icon even when the surface has been
   pushed left to stay on screen.

   The panel is one flat surface — no header band, no boxed fields. The
   only lines drawn are the panel's own edge and a single hairline above
   the action row; everything else is separated by space. Colour comes
   from tokens, so both themes follow.

   State lives on `html`, not on the overlay, because the trigger is
   outside it: `.feedback-open`, `.feedback-nudging` and
   `[data-feedback-draft]`.

   Stacking: above page content and the drawer backdrop (1039), below
   Bootstrap modals (1050), so a dialog still covers the panel.
   ============================================================================ */

.feedback-widget {
    /* Anchor, in viewport pixels, written by feedback.js. The fallbacks are
       only what the first frame uses, before it has measured anything. */
    --feedback-anchor-x: 50vw;
    --feedback-anchor-y: 62px;
    --feedback-tail-x: 22px;
    /* How far the surfaces stay clear of the viewport edges. */
    --feedback-panel-inset: 16px;
    --feedback-pad: 18px;
    --feedback-body-gutter: 8px; /* room for Send's shadow — see .feedback-panel */
    /* The widget's one blue fill: the chosen category chip and Send both wear
       it, so they read as the same control in two places. */
    --feedback-accent-fill: linear-gradient(
        180deg,
        rgba(var(--bs-primary-rgb), 0.28),
        rgba(var(--bs-primary-rgb), 0.16)
    );
    --feedback-accent-shadow:
        0 2px 6px rgba(0, 0, 0, 0.45),
        0 1px 0 rgba(255, 255, 255, 0.06) inset;
    position: fixed;
    left: var(--feedback-anchor-x);
    top: var(--feedback-anchor-y);
    /* A point, not a box: it must not intercept clicks on the page under it,
       and the surfaces it anchors are all absolutely positioned. */
    width: 0;
    height: 0;
    z-index: 1045;
    font-family: var(--font-family-base);
}

/* --- The topbar trigger ---------------------------------------------
   Chrome and colour come from .btn-ghost, like every other topbar icon.
   What is added here is the two things only this button says: an unsent
   draft, and a reminder waiting to be read.
   -------------------------------------------------------------------- */

.feedback-trigger {
    font-size: 1.05rem;
}

/* The mobile mount sits in the logo header, whose children are ordered:
   the sidenav hamburger is 1 and the topbar "more" toggle is 3. */
.logo-header .feedback-trigger {
    order: 2;
    margin-left: 0.25rem;
}

/* An unsent draft is worth a mark on the icon — it is the only clue left
   once the panel is closed and the user has navigated away. The ring is
   the topbar's own colour, so the dot reads as sitting on the chrome. */
html[data-feedback-draft="true"] .feedback-trigger::after {
    content: "";
    position: absolute;
    top: 7px;
    right: 7px;
    width: 10px;
    height: 10px;
    border: 2px solid var(--chrome-surface);
    border-radius: 50%;
    background: rgb(var(--color-orange-rgb));
}

/* --- Right-click menu ------------------------------------------------
   Placed at the pointer by `openContextMenu()`, so it takes no part in
   the widget's flex column despite sitting inside it.
   -------------------------------------------------------------------- */

.feedback-menu {
    position: fixed;
    z-index: 1046; /* over the panel, which it can be opened in front of */
    display: flex;
    flex-direction: column;
    min-width: 196px;
    padding: 6px;
    border: 1px solid var(--border-normal);
    border-radius: 12px;
    background: var(--bg-surface);
    box-shadow: var(--menu-shadow);
}

/* `display: flex` above would otherwise beat the browser's own
   `[hidden]` rule and leave the menu on screen permanently. */
.feedback-menu[hidden] {
    display: none;
}

.feedback-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-secondary);
    font-size: 0.78rem;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}

.feedback-menu-item i {
    color: var(--text-muted);
}

.feedback-menu-item:hover {
    background: var(--surface-2);
    color: var(--text-primary);
}

.feedback-menu-danger:hover {
    background: rgba(var(--color-red-rgb), 0.14);
}

.feedback-menu-danger:hover,
.feedback-menu-danger:hover i {
    color: rgb(var(--color-red-rgb));
}

/* --- The panel ------------------------------------------------------ */

/* Hangs from the anchor under the trigger and grows down and to the
   right, and can never leave the screen: `left` is clamped by
   `placeSurface()` in feedback.js, and max-width/max-height stop at the
   viewport. Only the inline width/height are set from the resize grip. */
.feedback-panel {
    position: absolute;
    /* Overwritten by `placeSurface()` the moment the panel opens; this is the
       unclamped position, the trigger's centre a tail-inset in from the left
       edge. */
    top: 0;
    left: calc(var(--feedback-tail-x) * -1);
    width: 430px;
    height: auto;
    /* Backstops only. The real floors are what the form needs — the category
       row on one line, and an empty message field that does not scroll — and
       `minPanelWidth()` / `minPanelHeight()` in feedback.js measure both. The
       contents are template data, so a number written here goes stale the
       moment a label or a line of help text changes. */
    min-width: 320px;
    min-height: 320px;
    max-width: calc(100vw - (2 * var(--feedback-panel-inset)));
    /* The anchor is what the panel hangs from, so the floor is the bottom of
       the viewport measured from there. */
    max-height: calc(100vh - var(--feedback-anchor-y) - var(--feedback-panel-inset));
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* The right side is a gutter short, and the body puts it back as its own
       padding: the body scrolls, so it clips on x, and Send sits flush
       against that edge. The gutter is what its shadow spills into.
       Content lands in the same place either way. */
    padding: var(--feedback-pad) calc(var(--feedback-pad) - var(--feedback-body-gutter))
             var(--feedback-pad) var(--feedback-pad);
    border: 1px solid var(--border-normal);
    border-radius: 16px;
    background: var(--bg-surface);
    box-shadow: var(--menu-shadow-lifted);
    transform-origin: top left;
    opacity: 0;
    transform: translateY(-8px) scale(.97);
    transition: opacity .18s ease, transform .18s ease,
                box-shadow .15s ease, border-color .15s ease;
}

/* While the user is typing in the panel it is the thing they are working in,
   not a popup resting over the page: the edge takes the accent and a soft ring
   sits outside it, so the panel is legible against whatever it is covering. */
.feedback-panel:focus-within {
    border-color: rgba(var(--color-blue-rgb), 0.4);
    box-shadow:
        var(--menu-shadow-lifted),
        0 0 0 1px rgba(var(--color-blue-rgb), 0.16);
}

html.feedback-open .feedback-panel {
    opacity: 1;
    transform: translateY(0) scale(1);
}

/* The panel's tail, drawn on the overlay rather than on the panel: the
   panel clips its own overflow, and this has to stand outside its top
   edge. The overlay's origin IS the trigger's centre, and the panel is
   only ever placed with that centre inside its own width, so a tail
   sitting on the origin always points at the icon and always meets the
   panel. Same square-turned-45° as the nudge's: the two bordered edges
   fall outside the panel, the two bare ones merge into its surface. */
.feedback-widget::after {
    content: "";
    position: absolute;
    top: -6px;
    left: -6px;
    width: 12px;
    height: 12px;
    border-left: 1px solid var(--border-normal);
    border-top: 1px solid var(--border-normal);
    border-top-left-radius: 3px;
    background: var(--bg-surface);
    transform: rotate(45deg);
    opacity: 0;
    pointer-events: none;
    transition: opacity .18s ease, border-color .15s ease;
}

html.feedback-open .feedback-widget::after {
    opacity: 1;
}

/* Follows the panel's own focus edge, since the two are one surface. */
.feedback-widget:focus-within::after {
    border-color: rgba(var(--color-blue-rgb), 0.4);
}

.feedback-panel.feedback-dropping {
    border-color: rgba(var(--color-blue-rgb), 0.6);
}

.feedback-panel-title {
    margin: 0 0 14px;
    padding-right: 40px; /* clears the minimize control */
    font-size: 0.9rem;
    font-weight: var(--font-weight-heading);
    letter-spacing: var(--heading-tracking);
    color: var(--text-primary);
}

/* Drag grip. Only the bottom-right corner can grow a panel pinned by its
   top-left to the trigger, so there is exactly one handle and no
   ambiguity about which edge moves. */
.feedback-resize {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 20px;
    height: 20px;
    cursor: nwse-resize;
    touch-action: none;
}

.feedback-resize::before {
    content: "";
    position: absolute;
    bottom: 7px;
    right: 7px;
    width: 8px;
    height: 8px;
    border-bottom: 2px solid var(--border-emphasis);
    border-right: 2px solid var(--border-emphasis);
    border-radius: 0 0 2px 0;
    transition: border-color .12s ease;
}

.feedback-resize:hover::before {
    border-color: var(--text-tertiary);
}

/* No easing mid-drag: the panel has to track the pointer exactly. */
.feedback-panel.feedback-resizing {
    transition: none;
    user-select: none;
}

.feedback-panel-controls {
    position: absolute;
    top: 11px;
    right: 11px;
    display: flex;
    gap: 2px;
}

.feedback-control {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border: 0;
    border-radius: 7px;
    background: transparent;
    color: var(--text-muted);
    font-size: 0.8rem;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}

.feedback-control:hover {
    background: var(--surface-2);
    color: var(--text-primary);
}

/* The body takes the slack, and the form passes it to the textarea, so
   dragging the panel taller buys writing room rather than empty space. */
.feedback-panel-body {
    flex: 1;
    min-height: 0;
    padding-right: var(--feedback-body-gutter);
    overflow-y: auto;
    overflow-x: hidden;
}

.feedback-form {
    display: flex;
    flex-direction: column;
    min-height: 100%;
}

/* --- Category segments ----------------------------------------------
   The audio widget's source tabs (tts/audio_source.css `.audio-tabs`),
   built from radios instead of buttons: same track, same chips, same
   selected wash, so choosing a category behaves like choosing between
   text-to-speech and an upload.
   -------------------------------------------------------------------- */

.feedback-segments {
    display: flex;
    margin: 0 0 16px;
    padding: 3px;
    border: 1px solid var(--border-subtle);
    border-radius: 9px;
    background: var(--surface-2);
    isolation: isolate;
}

.feedback-segment-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.feedback-segment {
    display: inline-flex;
    /* Grow from the label's own width, never from zero. Equal-width chips
       give "Question" the same box as "Bug", which eats its padding and
       makes the row look mis-set; growing from content adds the same
       number of spare pixels to each, so the padding reads as equal. */
    flex: 1 1 auto;
    align-items: center;
    justify-content: center;
    margin: 0 !important;
    padding: 0.28rem 0.45rem;
    border: 1px solid transparent;
    border-radius: 7px;
    background: transparent;
    /* !important throughout: these are <label> elements, and forms.css
       sets label colour, font-size and margin !important for real field
       labels. A tab is not a field label. */
    color: var(--text-tertiary) !important;
    /* A notch under the audio widget's 0.85rem: four chips share the width
       two source tabs have there, inside a 430px panel. */
    font-size: 0.8rem !important;
    white-space: nowrap;
    cursor: pointer;
    transition: color 0.18s ease, background-color 0.18s ease;
}

/* The glyph carries its own line box, which sits low against the label's
   text; centring it on the chip's optical middle means ignoring that box. */
.feedback-segment i {
    line-height: 1;
    font-size: 0.9em;
}

.feedback-segment-label {
    overflow: hidden;
    margin-left: 0.3rem;
    text-overflow: ellipsis;
}

.feedback-segment:hover {
    color: var(--text-secondary) !important;
}

.feedback-segment-input:checked + .feedback-segment {
    color: var(--color-blue-hover) !important;
    background: var(--feedback-accent-fill);
    border-color: transparent;
    box-shadow: var(--feedback-accent-shadow);
}

.feedback-segment-input:focus-visible + .feedback-segment {
    outline: 2px solid rgb(var(--color-blue-rgb));
    outline-offset: 1px;
}

/* --- Message ---------------------------------------------------------
   Deliberately unboxed: the panel already frames it, and a second border
   inside a bordered card is the thing that made this feel cluttered.
   -------------------------------------------------------------------- */

.feedback-textarea {
    display: block;
    flex: 1;
    width: 100%;
    /* Drives the panel's default height, which is `auto`, and its measured
       floor: the writing area is the only part that can be given more room. */
    min-height: 150px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--text-primary);
    font-size: 0.875rem;
    line-height: 1.55;
    resize: none;
}

.feedback-textarea::placeholder {
    color: var(--text-muted);
}

.feedback-textarea:focus {
    outline: 0;
}

/* --- Attachments ----------------------------------------------------- */

/* Beats `input[type="file"] { display: flex }` in forms.css, which is
   more specific than a bare class and would otherwise render the native
   control — the button and the drop zone are the interface here. */
.feedback-form input[type="file"].feedback-file-input {
    display: none;
}

.feedback-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 12px 0 0;
    padding: 0;
    list-style: none;
}

.feedback-thumbs:empty {
    margin: 0;
}

.feedback-thumb {
    position: relative;
    width: 54px;
    height: 54px;
    border: 1px solid var(--border-subtle);
    border-radius: 9px;
    overflow: hidden;
    background: var(--bg-inset);
}

.feedback-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.feedback-thumb-file {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    color: var(--text-tertiary);
    font-size: 0.62rem;
    font-weight: var(--font-weight-strong);
}

.feedback-thumb-remove {
    position: absolute;
    top: 3px;
    right: 3px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 17px;
    height: 17px;
    border: 0;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.7);
    color: #fff;
    font-size: 0.55rem;
    cursor: pointer;
}

.feedback-thumb-remove:hover {
    background: rgb(var(--color-red-fill-rgb));
}

/* --- Action row ------------------------------------------------------ */

.feedback-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border-subtle);
}

.feedback-tool {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-tertiary);
    font-size: 0.82rem;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}

.feedback-tool:hover {
    background: var(--surface-2);
    color: var(--text-primary);
}

.feedback-tool:focus-visible {
    outline: 2px solid rgb(var(--color-blue-rgb));
    outline-offset: 1px;
}

.feedback-submit {
    margin-left: auto;
    height: 32px;
    padding: 0 18px;
    border: 0;
    border-radius: 8px;
    background: var(--feedback-accent-fill);
    color: var(--color-blue-hover);
    font-size: 0.8rem;
    font-weight: var(--font-weight-control);
    box-shadow: var(--feedback-accent-shadow);
    cursor: pointer;
    transition: filter .12s ease;
}

.feedback-submit:hover {
    filter: brightness(1.08);
}

.feedback-submit:disabled {
    opacity: .6;
    cursor: default;
}

.feedback-note {
    margin: 10px 0 0;
    color: var(--text-muted);
    font-size: 0.68rem;
    line-height: 1.45;
}

.feedback-discard:hover {
    background: rgba(var(--color-red-rgb), 0.14);
    color: rgb(var(--color-red-rgb));
}

.feedback-error {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 12px;
    padding: 9px 11px;
    border-radius: 9px;
    background: rgba(var(--color-red-rgb), 0.14);
    color: var(--text-primary);
    font-size: 0.76rem;
    line-height: 1.45;
}

.feedback-error i {
    margin-top: 2px;
    color: rgb(var(--color-red-rgb));
}

/* --- Confirmation ---------------------------------------------------- */

.feedback-result {
    padding: 14px 4px 6px;
    text-align: center;
}

.feedback-result-mark {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    margin: 0 auto 12px;
    border-radius: 50%;
    background: rgba(var(--color-green-rgb), 0.16);
    color: rgb(var(--color-green-rgb));
}

.feedback-result-title {
    margin: 0 0 4px;
    color: var(--text-primary);
    font-size: 0.92rem;
    font-weight: var(--font-weight-strong);
}

.feedback-result-body {
    margin: 0 auto;
    max-width: 260px;
    color: var(--text-tertiary);
    font-size: 0.76rem;
    line-height: 1.5;
}

.feedback-result-again {
    margin-top: 14px;
    padding: 0;
    border: 0;
    background: transparent;
    color: rgb(var(--color-blue-rgb));
    font-size: 0.76rem;
    cursor: pointer;
}

.feedback-result-again:hover {
    text-decoration: underline;
}

/* --- Reminder nudge ---------------------------------------------------
   A message arriving from the topbar icon, so it is shaped like one: a
   card hanging below the icon with a tail pointing up at it, and no
   button — the message itself is what you click.
   -------------------------------------------------------------------- */

.feedback-nudge {
    position: absolute; /* hangs from the anchor, and anchors the tail */
    top: 0;
    left: calc(var(--feedback-tail-x) * -1); /* clamped by `placeSurface()` */
    display: flex;
    align-items: flex-start;
    gap: 2px;
    /* The overlay it hangs off is a zero-width point, so shrink-to-fit has no
       space to resolve against and would collapse every line to min-content.
       `max-content` sizes it to the message on one line; the cap below is what
       brings it back in on a narrow viewport. */
    width: max-content;
    max-width: calc(100vw - (2 * var(--feedback-panel-inset)));
    padding: 4px;
    border: 1px solid var(--border-normal);
    border-radius: 14px;
    background: var(--bg-surface);
    box-shadow: var(--menu-shadow);
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity .2s ease, transform .2s ease;
}

/* The tail: a square turned 45°, showing only the two edges that fall
   outside the card, centred on the trigger it came from. */
.feedback-nudge::after {
    content: "";
    position: absolute;
    left: calc(var(--feedback-tail-x) - 6px);
    top: -7px;
    width: 12px;
    height: 12px;
    border-left: 1px solid var(--border-normal);
    border-top: 1px solid var(--border-normal);
    border-top-left-radius: 3px;
    background: var(--bg-surface);
    transform: rotate(45deg);
}

/* Overshoots slightly on the way in: a card that simply fades down is
   the thing nobody noticed. Origin is the icon, so it grows out of it. */
@keyframes feedback-nudge-in {
    0% {
        opacity: 0;
        transform: translateY(-14px) scale(.88);
    }
    60% {
        opacity: 1;
        transform: translateY(3px) scale(1.03);
    }
    100% {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

/* Then it keeps bobbing, because a card that arrives once and goes still
   is missed by anyone who happened to be reading elsewhere on screen.
   Five rounds, with a long pause built into the keyframes, so it draws
   the eye repeatedly without ever becoming a flashing thing. */
@keyframes feedback-nudge-bob {
    0%, 55%, 100% {
        transform: translateY(0);
    }
    62% {
        transform: translateY(-7px);
    }
    72% {
        transform: translateY(0);
    }
    79% {
        transform: translateY(-4px);
    }
    88% {
        transform: translateY(0);
    }
}

html.feedback-nudging .feedback-nudge {
    opacity: 1;
    transform: translateY(0);
    transform-origin: var(--feedback-tail-x) 0;
    animation:
        feedback-nudge-in .42s cubic-bezier(.2, .9, .3, 1.25) both,
        feedback-nudge-bob 3.4s ease-in-out .6s 5;
}

/* One ring, expanding out of the icon the tail points at. */
@keyframes feedback-trigger-pulse {
    0% {
        box-shadow: 0 0 0 0 rgba(var(--color-blue-rgb), 0.5);
    }
    70% {
        box-shadow: 0 0 0 12px rgba(var(--color-blue-rgb), 0);
    }
    100% {
        box-shadow: 0 0 0 0 rgba(var(--color-blue-rgb), 0);
    }
}

/* Lit while the message is on screen, so the eye connects the two. */
html.feedback-nudging .feedback-trigger {
    color: var(--color-blue-hover);
    animation: feedback-trigger-pulse 1.9s ease-out 5;
}

/* The whole message is the button. */
.feedback-nudge-message {
    display: flex;
    align-items: center;
    gap: 13px;
    padding: 12px 14px;
    border: 0;
    border-radius: 12px;
    background: transparent;
    text-align: left;
    cursor: pointer;
    transition: background .12s ease;
}

.feedback-nudge-message:hover {
    background: var(--surface-2);
}

.feedback-nudge-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    background: rgb(var(--color-blue-fill-rgb));
    color: #fff;
    font-size: 0.95rem;
}

.feedback-nudge-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.feedback-nudge-title {
    color: var(--text-primary);
    font-size: 0.92rem;
    font-weight: var(--font-weight-strong);
    white-space: nowrap;
}

.feedback-nudge-text {
    color: var(--text-tertiary);
    font-size: 0.78rem;
    white-space: nowrap;
}

.feedback-nudge-dismiss {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    margin: 4px 2px 0 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font-size: 0.7rem;
    cursor: pointer;
}

.feedback-nudge-dismiss:hover {
    background: var(--surface-2);
    color: var(--text-primary);
}

/* --- Small screens --------------------------------------------------- */

@media (max-width: 575.98px) {
    .feedback-widget {
        --feedback-panel-inset: 12px;
    }

    /* Only the sub-line wraps; the title stays on one line so a two-line card
       still reads as a title with a message under it. */
    .feedback-nudge-text {
        white-space: normal;
    }

    /* Chrome shrinks with the card, so what space the cap leaves goes to the
       text rather than to padding around it. */
    .feedback-nudge-message {
        gap: 10px;
        padding: 10px 12px;
    }

    .feedback-nudge-avatar {
        width: 32px;
        height: 32px;
        font-size: 0.85rem;
    }
}

/* Motion is decorative here; a reduced-motion user still gets the
   opacity change so the panel's arrival is not silent. The tail is a
   `::after`, so the blanket `transform: none` cannot flatten it. */
@media (prefers-reduced-motion: reduce) {
    .feedback-panel,
    .feedback-nudge {
        transition: opacity .15s ease;
        transform: none !important; /* beats the scale/translate rest states above */
    }

    html.feedback-nudging .feedback-nudge,
    html.feedback-nudging .feedback-trigger {
        animation: none; /* including the bob, which is pure attention-getting */
    }
}
