/* ── SweetAlert compact ── */
.swal-compact.swal2-popup {
    font-size: 0.875rem !important;
    padding: 1.25rem 1.5rem;
    max-width: 360px;
}
.swal-compact .swal2-title { font-size: 1rem !important; }
.swal-compact .swal2-actions { margin-top: 0.75rem; }
.swal-compact .swal2-confirm,
.swal-compact .swal2-cancel { font-size: 0.85rem !important; padding: 0.35rem 1rem !important; }

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; padding: 0; overflow: hidden; }

/* Pinned on the horizontal axis only. `none` on both axes shipped 2026-09-01
   and lasted a day: it also removes pull-to-refresh, and that pull is the only
   reload gesture a phone has — Itay asked for it back the same evening. What
   returns with it, knowingly: the vertical rubber-band, and the risk that a
   pull at the top of the page reloads over an unsent draft in the composer. */
html, body { overscroll-behavior-x: none; }

/* ── CSS Variables ── */
:root {
    --bg-main: #f8f9fa;
    --bg-input-area: #fff;
    --bg-input-wrapper: #f8f9fa;
    --bg-bubble-assistant: #fff;
    --color-text: #222;
    --color-text-secondary: #666;
    --color-welcome-title: #1a1a2e;
    --border-color: #e0e0e0;
    --border-input: #ddd;
    --scrollbar-thumb: #ccc;
    --copy-btn-color: #aaa;
    --copy-btn-hover: #555;
    --hint-color: #6f6f78;   /* 4.98:1 on white */
    --code-bg: #f0f0f0;
    --pre-bg: #f5f5f5;
    --blockquote-border: #ccc;
    --blockquote-color: #666;
}
[data-theme="dark"] {
    --bg-main: #0f0f1a;
    --bg-input-area: #16162a;
    --bg-input-wrapper: #1e1e2e;
    --bg-bubble-assistant: #1e1e2e;
    --color-text: #e0e0e0;
    --color-text-secondary: #aaa;
    --color-welcome-title: #e0e0e0;
    --border-color: rgba(255,255,255,0.1);
    --border-input: rgba(255,255,255,0.12);
    --scrollbar-thumb: rgba(255,255,255,0.15);
    --copy-btn-color: #666;
    --copy-btn-hover: #aaa;
    --hint-color: #8b8b99;   /* 5.29:1 on the dark field, tinted from its plum */
    --code-bg: #2a2a3e;
    --pre-bg: #252535;
    --blockquote-border: #444;
    --blockquote-color: #888;
}

/* ── Layout ── */
.chat-layout {
    display: flex;
    /* 100vh is the LARGEST viewport — the height the page would have if the
       browser's URL bar were retracted. On Android Chrome with the bar showing,
       a 100vh shell is roughly 56px taller than what you can see, and since this
       is a fixed shell with overflow hidden there is no scrolling down to the
       part that fell off: the composer is simply not reachable. That is the bug
       this page shipped with. dvh is the CURRENT viewport and tracks the bar as
       it comes and goes. The vh line stays as the fallback for a browser without
       dvh, where being too tall beats being unstyled. */
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
}

/* ── Mobile top bar (hidden on desktop) ── */
.mobile-header {
    display: none;
    align-items: center;
    justify-content: space-between;
    /* Top inset for a notch or a status bar the page now draws under; the sides
       for a curved corner when the phone is held sideways. */
    padding: calc(0.6rem + var(--hg-edge-top)) calc(1rem + var(--hg-edge-right))
             0.6rem calc(1rem + var(--hg-edge-left));
    min-height: calc(56px + var(--hg-edge-top));
    background: #1e1e2e;
    border-bottom: 1px solid rgba(255,255,255,0.08);
    flex-shrink: 0;
    z-index: 10;
}
.mobile-logo {
    font-size: 1rem;
    font-weight: 700;
    color: #fff;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.btn-icon {
    background: none;
    border: none;
    color: rgba(255,255,255,0.7);
    cursor: pointer;
    font-size: 1.1rem;
    padding: 4px 8px;
    border-radius: 6px;
    line-height: 1;
    display: flex;
    align-items: center;
    transition: color 0.15s, background 0.15s;
}
.btn-icon:hover { color: #fff; background: rgba(255,255,255,0.1); }

/* ── Sidebar ── */
.sidebar {
    width: 260px;
    min-width: 260px;
    background: #1e1e2e;
    display: flex;
    flex-direction: column;
    border-left: 1px solid rgba(255,255,255,0.08);
    color: #e0e0e0;
    /* translate, not `right`. Animating an inset property makes the browser lay
       the panel out again on every frame; a transform is composited. The rabbi
       workspace already slides its index this way and this page is the one that
       did not. */
    transition: translate var(--hg-med, 0.25s) var(--hg-ease, ease);
}
.sidebar-header {
    padding: calc(1rem + var(--hg-edge-top)) 1rem 1rem;
    border-bottom: 1px solid rgba(255,255,255,0.08);
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.sidebar-logo {
    font-size: 1.1rem;
    font-weight: 700;
    color: #fff;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.btn-new-chat {
    background: rgba(255,255,255,0.1);
    border: 1px solid rgba(255,255,255,0.15);
    color: #e0e0e0;
    border-radius: 8px;
    padding: 0.5rem 0.75rem;
    font-size: 0.875rem;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    cursor: pointer;
    transition: background 0.15s;
    width: 100%;
}
.btn-new-chat:hover { background: rgba(255,255,255,0.18); color: #fff; }

.conv-list {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden; /* titles ellipsize; the list must never pan sideways */
    padding: 0.5rem;
}
.conv-list::-webkit-scrollbar { width: 4px; }
.conv-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 2px; }

.conv-item {
    padding: 0.6rem 0.75rem;
    border-radius: 8px;
    cursor: pointer;
    font-size: 0.85rem;
    color: #c0c0c0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    transition: background 0.15s;
    margin-bottom: 2px;
}
.conv-item:hover { background: rgba(255,255,255,0.08); color: #fff; }

/* Selection is a NEUTRAL fill, not a blue one.
   It used to be rgba(79,142,247,0.2), which lands on #283456 — and the
   "assigned" status fill landed on #243256. Selection and status were fighting
   over the same blue, which is most of why this list looked muddy. Colour in
   this list now means one thing: which state a thread is in. */
.conv-item.active { background: rgba(255,255,255,0.14); color: #fff; }
.conv-item.active .conv-item-title { font-weight: 600; }

.conv-item-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.conv-item-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conv-delete {
    background: none;
    border: none;
    color: rgba(255,255,255,0.3);
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 4px;
    font-size: 0.75rem;
    opacity: 0;
    transition: opacity 0.15s, color 0.15s;
}
.conv-item:hover .conv-delete,
.conv-item:focus-within .conv-delete { opacity: 1; }

/* On a touch screen there is no hover, so a control revealed by it does not
   exist. DESIGN.md says not to hide a primary action that way; this one is
   destructive rather than primary, so it stays quiet on a desk and is simply
   present on a phone. */
@media (hover: none) {
    .conv-delete { opacity: 0.55; }
}
.conv-delete:hover { color: #ff6b6b; }

.sidebar-footer {
    /* The last thing above the gesture bar when the drawer is open. */
    padding: 0.75rem 1rem calc(0.75rem + var(--hg-edge-bottom));
    border-top: 1px solid rgba(255,255,255,0.08);
    font-size: 0.8rem;
    color: rgba(255,255,255,0.5);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.sidebar-footer a { color: rgba(255,255,255,0.5); text-decoration: none; }
.sidebar-footer a:hover { color: #fff; }
.btn-darkmode {
    background: none;
    border: 1px solid rgba(255,255,255,0.15);
    color: rgba(255,255,255,0.6);
    border-radius: 6px;
    padding: 0.3rem 0.6rem;
    font-size: 0.8rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    transition: background 0.15s, color 0.15s;
    width: fit-content;
}
.btn-darkmode:hover { background: rgba(255,255,255,0.1); color: #fff; }

/* ── Text size ──
   In the sidebar, which is always dark chrome whatever the page theme is — so
   the inks here are unconditional. Keying them on [data-theme] is what made the
   status labels in this same panel unreadable at 2.07:1.

   The three buttons carry the same letter at three sizes: the control shows what
   it does without a legend, and א is the right letter to show it with on a
   Hebrew page. */
.text-scale { display: flex; flex-direction: column; gap: 0.3rem; }
.text-scale-title {
    font-size: 0.7rem;
    letter-spacing: 0.04em;
    color: rgba(255,255,255,0.55);      /* 5.6:1 on #1e1e2e */
}
.text-scale-steps {
    display: flex;
    align-items: stretch;
    gap: 2px;
    background: rgba(255,255,255,0.06);
    border-radius: 8px;
    padding: 2px;
    width: fit-content;
}
.text-scale-btn {
    min-width: 30px;
    background: none;
    border: none;
    border-radius: 6px;
    color: rgba(255,255,255,0.62);       /* 6.3:1 */
    cursor: pointer;
    line-height: 1;
    padding: 0.3rem 0.45rem;
    font-family: inherit;
    transition: background var(--hg-fast, 0.15s), color var(--hg-fast, 0.15s);
}
.text-scale-btn[data-text-step="normal"] { font-size: 0.8rem; }
.text-scale-btn[data-text-step="large"]  { font-size: 0.95rem; }
.text-scale-btn[data-text-step="larger"] { font-size: 1.15rem; }
.text-scale-btn:hover { color: #fff; background: rgba(255,255,255,0.10); }
.text-scale-btn.is-on {
    color: #fff;
    background: rgba(255,255,255,0.16);
    font-weight: 700;
}

@media (pointer: coarse) {
    .text-scale-btn { min-width: var(--hg-touch); min-height: var(--hg-touch); }
}

/* Sidebar overlay (mobile) */
.sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 99;
}
.sidebar-overlay.visible { display: block; }

/* ── Main area ── */
.chat-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    background: var(--bg-main);
    overflow: hidden;
}

/* ── Messages area ── */
.messages-area {
    /* --pad is the one place the figure lives, so the mobile step below changes
       a number and not a four-value shorthand with two calc() in it. */
    --pad: 1rem;
    flex: 1;
    overflow-y: auto;
    /* Stated, because leaving it out does not mean "visible": on a scroll
       container the other axis computes to auto, so one answer wider than a
       phone let the whole transcript pan sideways. Wide content scrolls inside
       its own bubble (the pre/table rules below); the transcript never does. */
    overflow-x: hidden;
    /* No overscroll-behavior here, on purpose: containing it also disables
       pull-to-refresh, and that pull is the only reload gesture a phone has.
       Tried 2026-09-01 and reverted the same evening — the asker losing a
       111-second wait to an accidental pull is the lesser cost. */
    padding: 1.5rem calc(var(--pad) + var(--hg-edge-right))
             1.5rem calc(var(--pad) + var(--hg-edge-left));
    display: flex;
    flex-direction: column;
}
.messages-area::-webkit-scrollbar { width: 6px; }
.messages-area::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 3px; }

/* ── Welcome screen ── */
.welcome-screen {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    color: var(--color-text-secondary);
    gap: 1rem;
    padding: 1rem calc(1rem + var(--hg-edge-right)) 1rem calc(1rem + var(--hg-edge-left));
}
/* Only the size moved out of an inline attribute. The colour stays inherited:
   this page draws its other two marks in plain white, and gold here would be a
   third treatment of one glyph. */
.welcome-mark { font-size: 3rem; line-height: 1; }
.welcome-screen h2 { font-size: 1.8rem; font-weight: 700; color: var(--color-welcome-title); }
.welcome-screen p { font-size: 0.95rem; max-width: 400px; }

/* ── Message bubbles ── */
.message-row {
    display: flex;
    gap: 0.75rem;
    max-width: 800px;
    width: 100%;
    margin: 1rem auto 0;
}
.message-row:first-child { margin-top: 0; }

/* Consecutive messages from one author are one utterance broken into parts, so
   only the first of a run is signed and the rest sit close under it. Five
   replies from a rabbi were five full letterheads, each one wider than the two
   words beneath it — the signature is what made a short answer look broken, and
   grouping is what stops it repeating. Spacing rather than gap on the flex
   parent, because a uniform gap cannot say "these belong together". */
.message-row.is-grouped { margin-top: 0.2rem; }
.message-row.is-grouped .msg-who { display: none; }
.message-row.is-grouped .message-bubble { padding-top: 0.6rem; padding-bottom: 0.6rem; }
/* Alignment says who is writing relative to the reader. In RTL the reader's own
   messages sit on the start edge — the right — the way every Hebrew messaging
   app puts them. The asker's question is therefore on the right here, and the
   machine and the rabbi are opposite; the rabbi's workspace mirrors it, with the
   psak on the right and the שואל on the left. Same rule, different reader.
   This used to be reversed: the asker's own question sat on the left. */
.message-row.user { flex-direction: row; }
.message-row.assistant,
.message-row.rabbi { flex-direction: row-reverse; }

.message-bubble {
    max-width: 75%;
    padding: 0.75rem 1rem;
    border-radius: 16px;
    font-size: 0.95rem;
    line-height: 1.6;
    direction: auto;
    word-break: break-word;
}
/* One corner collapses toward its author. Logical properties, so the tail
   follows the writing direction and the rule above without a second copy. */
.message-row.user .message-bubble {
    background: #0d6efd;
    color: #fff;
    border-end-start-radius: 4px;
}
.message-row.assistant .message-bubble {
    background: var(--bg-bubble-assistant);
    color: var(--color-text);
    border: 1px solid var(--border-color);
    border-end-end-radius: 4px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.05);
    min-width: min(100%, 12rem);
}

/* THE PSAK, on the asker's side.
   There was no rule for this at all: chat.js has always emitted
   `message-row rabbi`, nothing styled it, and .message-bubble carries no
   background of its own — so the single most important message in the product
   arrived as unformatted text on the page background. Same identity a rabbi
   sees in their own workspace: a warm sheet, a gold hairline, and the name at
   the head with a rule under it. */
.message-row.rabbi .message-bubble {
    background: var(--hg-psak-bg);
    color: var(--hg-psak-ink);
    border: 1px solid var(--hg-psak-edge);
    border-end-end-radius: 4px;
    box-shadow: var(--hg-shadow);
    padding: 1rem;
    font-size: 1rem;
    line-height: 1.8;
    /* The bubble hugs its content, and the signature is wider than a short
       answer — which turned "סבבה" into a tall narrow tower with a header on
       top. min() so it still shrinks on a narrow phone. */
    min-width: min(100%, 15rem);
}
.message-row.rabbi .msg-who {
    display: flex;
    align-items: center;
    gap: 0.4em;
    padding-bottom: 0.5rem;
    margin-bottom: 0.75rem;
    border-bottom: 1px solid var(--hg-psak-rule);
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--hg-psak-name);
}
.message-row.rabbi .msg-who .msg-mark { color: var(--hg-psak-name); font-size: 1.15em; }
.message-row.rabbi .message-bubble a { color: var(--hg-psak-link); }

/* The machine names itself too, so the asker can tell a provisional answer from
   a psak without relying on colour. */
.message-row.assistant .msg-who {
    display: flex;
    align-items: center;
    gap: 0.4em;
    margin-bottom: 0.35rem;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    color: var(--color-text-secondary);
}

/* Markdown styles */
.message-bubble h1,.message-bubble h2,.message-bubble h3 { margin-top: 0.75rem; margin-bottom: 0.4rem; font-size: 1rem; font-weight: 700; }
.message-bubble p { margin-bottom: 0.5rem; }
.message-bubble p:last-child { margin-bottom: 0; }
.message-bubble ul,.message-bubble ol { padding-right: 1.25rem; margin-bottom: 0.5rem; }
.message-bubble li { margin-bottom: 0.25rem; }
.message-bubble code { background: var(--code-bg); padding: 0.1em 0.35em; border-radius: 4px; font-size: 0.875em; }
.message-bubble pre { background: var(--pre-bg); border-radius: 8px; padding: 0.75rem; overflow-x: auto; font-size: 0.85em; }
/* marked runs with gfm on, so an answer can carry a table — and a table lays
   out at its content's width, ignoring max-width. display:block restores it,
   and the table then scrolls inside its own bubble like pre above. The admin
   conversation reader loads this sheet too, so it is covered by the same rule. */
.message-bubble table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
    font-size: 0.9em;
}
.message-bubble th,
.message-bubble td { border: 1px solid var(--border-color); padding: 0.3em 0.6em; }
.message-bubble img { max-width: 100%; height: auto; }
.message-bubble blockquote { border-inline-start: 3px solid var(--blockquote-border); padding-inline-start: 0.75rem; color: var(--blockquote-color); margin: 0.5rem 0; }
.message-bubble strong { font-weight: 700; }

/* ── Copy button ── */
.copy-btn-row {
    margin-top: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    text-align: left;
}
.copy-btn {
    background: none;
    border: none;
    color: var(--copy-btn-color);
    cursor: pointer;
    font-size: 0.78rem;
    padding: 2px 4px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    transition: color 0.15s;
}
.copy-btn:hover { color: var(--copy-btn-hover); }
.copy-btn.copied { color: #28a745; }

/* ── Loading dots ── */
.loading-dots { display: flex; gap: 4px; align-items: center; padding: 0.5rem 0; }
.loading-dots span {
    width: 8px; height: 8px; background: #999;
    border-radius: 50%;
    animation: bounce 1.2s infinite ease-in-out;
}
.loading-dots span:nth-child(1) { animation-delay: 0s; }
.loading-dots span:nth-child(2) { animation-delay: 0.2s; }
.loading-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes bounce {
    0%,80%,100% { transform: translateY(0); }
    40% { transform: translateY(-8px); }
}

/* ── Input area ── */
.input-area {
    --pad: 1rem;
    background: var(--bg-input-area);
    border-top: 1px solid var(--border-color);
    /* The bottom inset is the fix for the Android navigation bar. The surface
       still runs to the physical edge — that is what viewport-fit=cover is for,
       and a composer that stops short of the glass looks like a letterboxed web
       page — but the controls inside it sit clear of the bar. */
    padding: var(--pad) calc(var(--pad) + var(--hg-edge-right))
             calc(var(--pad) + var(--hg-edge-bottom)) calc(var(--pad) + var(--hg-edge-left));
}
.input-wrapper {
    max-width: 800px;
    margin: 0 auto;
    display: flex;
    align-items: flex-end;
    gap: 0.5rem;
    background: var(--bg-input-wrapper);
    border: 1px solid var(--border-input);
    border-radius: 12px;
    padding: 0.5rem 0.75rem;
    transition: border-color 0.2s, box-shadow 0.2s;
}
.input-wrapper:focus-within {
    border-color: #0d6efd;
    box-shadow: 0 0 0 3px rgba(13,110,253,0.1);
}
#messageInput {
    flex: 1;
    border: none;
    background: transparent;
    resize: none;
    outline: none;
    font-size: 0.95rem;
    line-height: 1.5;
    max-height: 200px;
    min-height: 40px;
    direction: auto;
    padding: 0.25rem 0;
    font-family: inherit;
    color: var(--color-text);
}
#messageInput::placeholder { color: var(--hint-color); }
.btn-send {
    width: 36px;
    height: 36px;
    background: #0d6efd;
    border: none;
    border-radius: 8px;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    flex-shrink: 0;
    transition: background 0.15s;
    font-size: 1rem;
}
.btn-send:hover { background: #0b5ed7; }
.btn-send:disabled { background: #aaa; cursor: not-allowed; }
.input-hint {
    text-align: center;
    font-size: 0.75rem;
    color: var(--hint-color);
    margin-top: 0.4rem;
    max-width: 800px;
    margin-left: auto;
    margin-right: auto;
}

/* ── Mobile ── */
@media (max-width: 768px) {
    .mobile-header { display: flex; }
    .chat-main { padding-top: 0; }

    .sidebar {
        position: fixed;
        top: 0;
        right: 0;
        height: 100vh;
        height: 100dvh;
        /* Off to its own start edge. The document is dir="rtl", so the drawer
           lives on the right and a positive translate takes it away. */
        translate: 100% 0;
        z-index: 100;
        border-left: none;
        border-right: 1px solid rgba(255,255,255,0.08);
    }
    .sidebar.open { translate: 0 0; }

    .message-bubble { max-width: 90%; }
    .message-row.user .message-bubble { max-width: 90%; }

    .messages-area { --pad: 0.75rem; padding-top: 1rem; padding-bottom: 1rem; }
    .input-area { --pad: 0.75rem; }
    .welcome-screen h2 { font-size: 1.4rem; }
}

/* ── Desktop: hide mobile header ── */
@media (min-width: 769px) {
    .mobile-header { display: none !important; }
    .sidebar-overlay { display: none !important; }
}

/* ── Touch ──
   Keyed on the INPUT METHOD, not the screen width. A laptop with a touchscreen
   has fingers on it and a phone held sideways is wider than the mobile
   breakpoint, so a width query gets both cases wrong. --hg-touch is 44px, the
   floor a fingertip needs; everything below was measured under it on a Pixel-
   sized viewport before this block existed — the send button at 36, the two bar
   buttons at 34x27, the field at 40.

   Note what is NOT here: nothing is hidden and nothing is rearranged. A phone
   gets the same page with controls a thumb can actually land on. */
@media (pointer: coarse) {
    .btn-send { width: var(--hg-touch); height: var(--hg-touch); }

    #messageInput {
        min-height: var(--hg-touch);
        /* 16px exactly, and this is the reason: below it, iOS Safari zooms the
           page the moment the field takes focus and never zooms back out,
           leaving the composer half off the screen for the rest of the visit. */
        font-size: 1rem;
    }

    .btn-icon {
        min-width: var(--hg-touch);
        min-height: var(--hg-touch);
        justify-content: center;
    }

    .btn-new-chat  { min-height: var(--hg-touch); }
    .btn-darkmode  { min-height: var(--hg-touch); padding-inline: 0.75rem; }
    .conv-item     { min-height: var(--hg-touch); }
    .sidebar-link,
    .btn-logout    { min-height: var(--hg-touch); }

    /* The target grows, the ink does not. This one sits inside a row that is
       itself tappable, so it needs to be findable by a thumb and hard to hit by
       accident: 44px of target, and clear space between it and the title. */
    .conv-delete {
        min-width: var(--hg-touch);
        min-height: var(--hg-touch);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        margin-inline-start: 0.25rem;
    }

    /* Copy sits under every answer at 0.78rem, about 19px tall. Padding it to
       44 would add a thumb's worth of empty space beneath every bubble and
       loosen the transcript — the row is the wrong thing to grow. So the hit
       area is grown instead, by a pseudo-element that takes part in no layout:
       the button still measures 19px, a finger gets 44. */
    .copy-btn { position: relative; }
    .copy-btn::after {
        content: '';
        position: absolute;
        inset-inline-start: 50%;
        top: 50%;
        translate: -50% -50%;
        width: max(100%, var(--hg-touch));
        height: var(--hg-touch);
    }
}

/* ── The composer's foot ──
   Enter and Shift+Enter mean nothing on a phone, and the whole line used to be
   `display: none` below the breakpoint — which took the daily counter with it.
   An asker on a phone therefore had no idea how many questions were left and
   met the refusal with no warning at all, on a product whose budget is 20 a day.
   The keys hide; the count does not. */
.input-hint {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.15rem 0.6rem;
}
@media (pointer: coarse) {
    .input-hint-keys { display: none; }
}

/* ── Sidebar footer controls ──
   These carried their layout in inline style attributes, which no stylesheet can
   reach — including the touch block above, which is why they are here now. */
/* All three footer links are secondary navigation in dark chrome, so they share
   one ink. The 0.85-vs-0.5 split they had was an accident of a single inline
   style attribute on one of them, not a hierarchy anyone chose. Specific enough
   to beat `.sidebar-footer a`. */
.sidebar-footer .sidebar-link {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    color: rgba(255,255,255,0.85);
    text-decoration: none;
}
.sidebar-footer .sidebar-link:hover { color: #fff; }
.btn-logout {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    background: none;
    border: none;
    padding: 0;
    font-size: 0.8rem;
    color: rgba(255,255,255,0.5);
    cursor: pointer;
}
.btn-logout:hover { color: #fff; }

@media (prefers-reduced-motion: reduce) {
    .sidebar { transition: none; }
    .loading-dots span { animation: none; }
}
[data-theme="dark"] .popover-body { color: #cbd5e1; }
[data-theme="dark"] .popover .popover-arrow::before { border-top-color: rgba(255,255,255,0.12); border-bottom-color: rgba(255,255,255,0.12); }
[data-theme="dark"] .popover .popover-arrow::after { border-top-color: #1e1e2e; border-bottom-color: #1e1e2e; }

/* ── Citation popover copy button ── */
.src-copy-btn {
    display: block;
    margin-top: 8px;
    padding: 2px 8px;
    cursor: pointer;
    border: 1px solid #ccc;
    border-radius: 4px;
    background: #f8f8f8;
    color: #444;
    font-size: 0.85rem;
    line-height: 1.6;
}
.src-copy-btn:hover { background: #e8e8e8; }
[data-theme="dark"] .src-copy-btn {
    background: #2a2a3e;
    border-color: rgba(255,255,255,0.15);
    color: #cbd5e1;
}
[data-theme="dark"] .src-copy-btn:hover { background: #353550; }

/* ── Time and days ──
   A thread with a rabbi spans hours or days, so a message without a time is a
   message whose place in the conversation the reader has to guess. Set quiet and
   inline at the end of the bubble, so it reads as a footnote to the message
   rather than as a field of its own. */
/* 0.72 rather than 0.6, and full strength on the asker's blue.
   A timestamp is incidental, but it is also SMALL text, and WCAG's 3:1
   allowance is for large text only — so it needs 4.5:1 like body copy. Measured:
   0.6 gave 4.09:1 on the psak's ivory and 3.95:1 on the asker's blue, where
   white at 100% is exactly 4.50:1 and there is no headroom to spend. */
.msg-time {
    display: block;
    margin-top: 0.35rem;
    flex-shrink: 0;
    font-size: 0.68rem;
    font-variant-numeric: tabular-nums;
    text-align: left;                 /* the end edge, in RTL */
    opacity: 0.72;
    line-height: 1;
}
.message-row.user .msg-time { color: #fff; opacity: 1; }
.copy-btn-row .msg-time { margin-top: 0; }

.day-rule {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    max-width: 800px;
    width: 100%;
    margin: 1.5rem auto 0.25rem;
    color: var(--color-text-secondary);
    font-size: 0.72rem;
    font-weight: 600;
}
.day-rule::before,
.day-rule::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border-color);
}

/* ── Where a referred question stands ──
   Not styled as an error and not styled as a success: a question with a rabbi is
   an ordinary state of this product, and the state colours already mean what
   they mean everywhere else — gold waits, blue is being worked on, green is
   finished. */
.thread-banner {
    /* Centred and only as wide as its words, not the full measure: at the
       composer's width and directly above it, it read as a second input field. */
    margin: 0 auto 0.7rem;
    width: fit-content;
    max-width: 100%;
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.3rem 0.85rem;
    border-radius: var(--hg-r-pill);
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.5;
}
.thread-banner i { flex-shrink: 0; font-size: 0.9rem; }

.thread-banner-pending_rabbi {
    background: var(--hg-waiting-fill);
    border: 1px solid var(--hg-waiting-edge);
    color: var(--hg-waiting-ink);
}
.thread-banner-assigned {
    background: var(--hg-working-fill);
    border: 1px solid rgba(59, 130, 246, 0.32);
    color: var(--hg-working-ink);
}
.thread-banner-closed {
    background: var(--hg-done-fill);
    border: 1px solid var(--hg-done-edge);
    color: var(--hg-done-ink);
}

/* A closed thread's composer. Disabled rather than hidden, so the reason it
   cannot be used stays where the asker is looking. */
#messageInput:disabled { cursor: not-allowed; opacity: 0.7; }

/* ── Daily budget notice ──
   Raised by the server's 429/503, which carry the wording and the reset time.
   Deliberately not styled as an error: hitting a daily limit is a normal state. */
.quota-notice {
    margin: 0 auto 0.6rem;
    max-width: 48rem;
    padding: 0.5rem 0.85rem;
    border-radius: 10px;
    background: rgba(255, 193, 7, 0.12);
    border: 1px solid rgba(255, 193, 7, 0.35);
    color: #8a6100;
    font-size: 0.85rem;
    text-align: center;
}
[data-theme="dark"] .quota-notice { color: #ffd968; }

/* Same element, different tone. A spent budget is a normal state with a reset
   time; a failed send is something going wrong, and reading as amber-ish
   "information" is how a reader concludes their question went through. */
.quota-notice.is-error {
    background: rgba(217, 56, 74, 0.10);
    border-color: rgba(217, 56, 74, 0.35);
    color: #a3202f;
}
[data-theme="dark"] .quota-notice.is-error { color: #ff9aa5; }

/* A thread with a rabbi: labelled in the sidebar, and not deletable there. */
/* A thread with a rabbi, labelled in the sidebar.
   
   THE SIDEBAR IS ALWAYS DARK — #1e1e2e, whatever the page theme is — so these
   inks can never be theme-conditional. They were, and the result was the
   LIGHT-mode inks on a dark ground in the default theme: measured at 2.07:1 for
   waiting, 1.88:1 for assigned and 2.29:1 for closed. All three below even the
   3:1 floor for large text. The on-dark values below are 11.4, 9.1 and 11.7.
   
   No pill either. A filled chip on a dark row reads as a second selection state
   and needs contrast against its own fill as well as the row; a dot plus a
   coloured word needs neither and says the same thing more quietly. */
.conv-item-status {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.68rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.conv-dot {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}
.conv-item-status.status-pending_rabbi { color: #fcd34d; }
.conv-item-status.status-assigned      { color: #93c5fd; }
.conv-item-status.status-closed        { color: #86efac; }
