/* The thread: one conversation, drawn as a constellation.
 *
 * One line runs down the page. A warm star where I speak, a pale ring where
 * someone speaks to me, a diamond where a job closed without a word, the moon
 * at its phase where a day turns. It ends at the composer in an empty ring:
 * your turn.
 *
 * The thread holds what was said, and nothing I wrote for myself. While a
 * turn runs there is one line for what I am doing (looking, remembering,
 * thinking, working, a small star for each step); when it ends the line goes
 * and the reply is what is left.
 *
 * What app.js builds (the classes are its):
 *   .thread-day                      a day turns (its moon, its date)
 *   .message.user                    what a person said
 *   .message.assistant.said          what I said to them (reply, send_message,
 *                                    a fallback, which also has .fallback-note)
 *   .message.assistant (not .said)   a turn from before the one output rule
 *                                    (its text was the answer), or the bubble
 *                                    a running turn's status line stands in
 *   .message-status                  that line
 *   .message.job-record              the collapsed "finished" line
 *
 * Geometry: the thread's left padding is --gutter; the rail stands at
 * --rail-x inside it. Every .message draws its own piece of the rail
 * (::before), reaching up through the gap above it, so the line is continuous
 * whatever the thread holds, and hangs its node level with its first line
 * (--node-y).
 */

.chat-container {
    --gutter: 96px;
    --rail-x: 78px;
    --gap: 20px;
    --fs: 1.03rem;
    --lh: 1.62;
    --pad-right: 22px;
    /* centre of a first line of body text, from the top of its message */
    --node-y: calc(var(--fs) * var(--lh) / 2);
    /* where the smoke behind the words begins to thin out again: just past
       the measure, so it is gone by the pane's edge */
    --smoke-end: 120px;
}

/* A narrow pane (a phone, or the conversation beside a working page) keeps
   the rail close to its edge. */
.chat-sidebar .chat-container {
    --gutter: 44px;
    --rail-x: 20px;
    --gap: 18px;
    --pad-right: 16px;
    --smoke-end: 0px;
}

@media (max-width: 767px) {
    .chat-container {
        --gutter: 44px;
        --rail-x: 20px;
        --gap: 18px;
        --fs: 1.0625rem; /* 17px */
        --pad-right: 16px;
        --smoke-end: 0px;
    }
}

.chat-header { display: none; }

/* ===== The scroller ===== */

.messages {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--gap);
    padding: 26px var(--pad-right) 18px var(--gutter);
    overflow-y: auto;
    /* Vertical only, said out loud: with overflow-x left to compute as auto,
       WebKit takes any wide child as a second axis and latches a slightly
       diagonal swipe onto it ("won't scroll" on an iPhone, August). */
    overflow-x: hidden;
    touch-action: pan-y;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    /* Its own layer, so scrolled text never rasters on the main thread over
       the live canvas beneath. */
    will-change: transform;
    contain: paint;
    /* The rail runs through open sky; the words stand on a little smoke, so
       nothing bright behind the lens can sit under a line of text. By day
       the frost already sees to that and there is none. */
    background: linear-gradient(90deg,
        transparent calc(var(--gutter) - 26px),
        rgba(var(--lens-rgb), var(--smoke-a)) calc(var(--gutter) - 2px),
        rgba(var(--lens-rgb), var(--smoke-a)) calc(100% - var(--smoke-end)),
        transparent calc(100% - var(--smoke-end) + 110px));
    /* The thread rises out of the frame's edge rather than being cut by it. */
    -webkit-mask-image: linear-gradient(180deg, transparent 4px, #000 30px);
    mask-image: linear-gradient(180deg, transparent 4px, #000 30px);
}

/* ===== A message, and its piece of the rail ===== */

.message {
    position: relative;
    display: flex;
    flex-direction: column;
    align-self: stretch;
}

.messages > .message::before {
    content: "";
    position: absolute;
    left: calc(var(--rail-x) - var(--gutter));
    top: calc(-1 * var(--gap));
    bottom: 0;
    width: 1px;
    background: var(--rail);
    /* by day the line is cut into the glass: a shaded edge beside the lit one */
    box-shadow: -1px 0 0 var(--rail-shade);
    pointer-events: none;
}

.messages > .message:first-child::before,
.messages > .thread-day + .message::before {
    top: 0;
}

/* It runs on to the composer. */
.messages > .message:last-child::before {
    bottom: -18px;
}

/* A node: centred on the rail, level with the first line. */
.messages > .message::after,
.messages > .message > .message-content::before,
.message .text-content::before {
    content: none;
    position: absolute;
    left: calc(var(--rail-x) - var(--gutter) + 0.5px);
    top: var(--node-y);
    transform: translate(-50%, -50%);
    pointer-events: none;
}

.message-content {
    max-width: 41rem;
    font-size: var(--fs);
    line-height: var(--lh);
    color: var(--ink);
    text-shadow: 0 0 10px rgba(var(--lens-rgb), 0.9), 0 0 2px rgba(var(--lens-rgb), 0.8);
}

.message .text-content {
    position: relative;
}

.text-content.entering {
    animation: text-arrive 0.35s ease;
}

@keyframes text-arrive {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: translateY(0); }
}

.message-header {
    font-size: 0.75rem;
    color: var(--ink-3);
}

/* The hour, in plain figures. */
.message-time {
    margin-left: 0.7em;
    font-family: var(--sans);
    font-style: normal;
    font-weight: 400;
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
    color: var(--ink-3);
}

/* The same minute as the message above says nothing new. */
.message.same-minute > .message-header .message-time {
    display: none;
}

/* ----- What I said: the warm star ----- */

.message.assistant > .message-header {
    display: none;
}

.messages > .message.said > .message-content::before,
.message.assistant:not(.said) .text-content::before {
    content: "";
    width: 19px;
    height: 19px;
    background: var(--star) center / contain no-repeat;
    /* lit by day (a shadow on the glass), a light by night */
    filter: drop-shadow(var(--cast)) drop-shadow(0 0 6px rgba(var(--gilt-rgb), var(--glow-a)));
}

/* A fallback reached them, but not because I sent it: the star is hollow. */
.messages > .message.said:has(.fallback-note) > .message-content::before {
    background-image: var(--star-hollow);
    filter: drop-shadow(var(--cast));
}

.fallback-note {
    margin-top: 5px;
    font-family: var(--serif);
    font-style: italic;
    font-weight: 600;
    font-size: 1.04rem;
    line-height: 1.3;
    color: var(--gilt);
}

/* A turn's own bubble with nothing in it takes no room. */
.message.assistant:not(.reply):has(> .message-content:empty) {
    display: none;
}

/* ----- What a person said: the pale ring ----- */

.messages > .message.user > .message-content::before {
    content: "";
    width: 13px;
    height: 13px;
    border-radius: 50%;
    border: 2px solid var(--steel);
    /* a band of steel: lighter where the light lands, darker beneath */
    border-color: #dbe9ff #a4c8ff #7ea9ea #bcd7ff;
    background: rgb(var(--ground-rgb));
    box-shadow:
        var(--cast),
        inset 0 1px 1.5px rgba(0, 0, 14, 0.7),
        0 0 8px rgba(var(--steel-rgb), calc(var(--glow-a) * 0.55));
    top: 0.74rem;
}

.message.user > .message-header {
    display: block;
    margin: 0 0 1px;
    font-family: var(--serif);
    font-style: italic;
    font-weight: 600;
    font-size: 1.16rem;
    line-height: 1.48rem;
    color: var(--steel);
}

.message.user > .message-content {
    color: var(--ink-2);
    font-size: calc(var(--fs) - 0.03rem);
    line-height: 1.55;
}

/* Said on Telegram: a small plane at the line's far end. */
.channel-badge {
    position: absolute;
    top: 0.3rem;
    right: 0;
    color: var(--steel);
    cursor: default;
}

.channel-badge .icon { width: 15px; height: 15px; }

.message:has(> .channel-badge) .message-content {
    padding-right: 24px;
}

/* On a wide pane the rail is also an axis: the hour stands to its left, at
   a star or a ring, wherever the minute has changed. app.js puts it in
   data-time; drawn as generated text, it is not part of what was said. */
@media (min-width: 768px) {
    .layout-centered .messages > .message[data-time]::after {
        content: attr(data-time);
        left: calc(-1 * var(--gutter));
        top: calc(var(--node-y) - 0.55rem);
        transform: none;
        width: calc(var(--rail-x) - 15px);
        text-align: right;
        font-size: 0.72rem;
        line-height: 1.1rem;
        font-variant-numeric: tabular-nums;
        letter-spacing: 0.04em;
        white-space: nowrap;
        color: var(--ink-3);
    }

    .layout-centered .messages > .message.user[data-time]::after {
        top: 0.19rem;
    }

    .layout-centered .message.user > .message-header .message-time {
        display: none;
    }
}

/* My reaction to what they said, set under it: a slip. */
.reaction-chip {
    align-self: flex-start;
    margin-top: 5px;
    padding: 3px 13px 3px 9px;
    border: 1px solid transparent;
    border-image: var(--frame-slip) 8 fill / 8px;
    font-size: 0.9rem;
    line-height: 1.2;
    cursor: default;
    user-select: none;
}

/* A reply that came from a model other than the usual one says so. */
.model-chip {
    align-self: flex-start;
    order: 3;
    margin-top: 6px;
    padding: 4px 14px 4px 10px;
    border: 1px solid transparent;
    border-image: var(--frame-slip) 8 fill / 8px;
    color: var(--ink-2);
    font-size: 0.75rem;
    line-height: 1.2;
    white-space: nowrap;
    cursor: default;
    user-select: none;
}

.message.anchor-focus {
    outline: 1px solid rgba(var(--gilt-rgb), 0.7);
    outline-offset: 8px;
}

/* ----- What I am doing right now: the growing end of the line ----- */

/* One line while a turn runs (app.js): a small drawing standing on the rail
   where the next star will be, the word for what I am doing, the chart
   symbol of each step taken so far (a triangle where I ran something, a
   square where I read, a ring where I searched; the one in hand in brass)
   and that step's name. Nothing of it is kept: when the turn ends it goes. */
.message-status {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 12px;
    min-height: 44px;
    font-family: var(--serif);
    font-style: italic;
    font-weight: 600;
    font-size: 1.24rem;
    line-height: 1.3;
    color: var(--gilt);
    text-shadow: 0 0 8px rgba(var(--lens-rgb), 0.9);
}

.constellation-mini {
    display: block;
    width: 56px;
    height: 36px;
    --constellation-star: 244, 228, 190;
    --constellation-line: 200, 215, 255;
}

.ripple-mini { display: block; }

.message-status-icon {
    position: absolute;
    left: calc(var(--rail-x) - var(--gutter) + 0.5px);
    top: 22px;
    transform: translate(-50%, -50%);
    display: grid;
    place-items: center;
    width: 56px;
    height: 36px;
    background: radial-gradient(closest-side, rgba(var(--ground-rgb), 0.9) 30%, transparent);
}

.message-status-text { padding-left: 14px; /* clear of the drawing */ }

.message-status-text:empty,
.message-status-steps:empty,
.message-status-step:empty {
    display: none;
}

.message-status-steps {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 5px;
    color: rgba(214, 226, 255, 0.9);
}

.message-status-steps .icon {
    width: 11px;
    height: 11px;
    stroke-width: 2.3;
    filter: drop-shadow(var(--cast));
}

.message-status-steps i.going {
    color: var(--gilt);
    animation: status-pulse 1.5s ease-in-out infinite;
}

.message-status-steps .more,
.message-status-step {
    font: var(--caps-small);
    font-variant-numeric: lining-nums;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--ink-2);
}

.message-status-steps .more { margin-right: 4px; letter-spacing: 0.08em; }

.status-dot.pulsing {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--gilt);
    box-shadow: 0 0 0 4px rgb(var(--ground-rgb));
    animation: status-pulse 1.5s ease-in-out infinite;
}

@keyframes status-pulse {
    0%, 100% { opacity: 0.45; transform: scale(1); }
    50% { opacity: 1; transform: scale(1.3); }
}

@keyframes orbit {
    to { transform: rotate(360deg); }
}

/* ----- The job line: a hollow diamond ----- */

.messages > .message.job-record::after {
    content: "";
    width: 9px;
    height: 9px;
    border: 1.5px solid rgba(214, 226, 255, 0.85);
    background: rgb(var(--ground-rgb));
    box-shadow: var(--cast);
    transform: translate(-50%, -50%) rotate(45deg);
    top: 0.86rem;
}

.messages > .message.job-record:has(details[open])::after {
    background: rgba(214, 226, 255, 0.85);
}

.message.job-record {
    font-size: 0.84rem;
    color: var(--ink-3);
}

.job-record summary {
    display: flex;
    align-items: baseline;
    min-height: 1.72rem;
    line-height: 1.72rem;
    letter-spacing: 0.02em;
    list-style: none;
    cursor: pointer;
    user-select: none;
    text-shadow: 0 0 8px rgba(var(--lens-rgb), 0.9);
}

.job-record summary::-webkit-details-marker { display: none; }

.job-record-label { min-width: 0; }
.job-record .message-time { white-space: nowrap; }
.chat-sidebar .job-record .message-time { display: none; }

.job-record summary::after {
    content: "";
    align-self: center;
    flex-shrink: 0;
    width: 6px;
    height: 6px;
    margin-left: 0.9em;
    border: solid currentColor;
    border-width: 1.5px 1.5px 0 0;
    transform: rotate(45deg);
    transition: transform 0.15s ease;
}

.job-record details[open] summary::after { transform: rotate(135deg); }
.job-record summary:hover { color: var(--ink); }

.job-record-body {
    max-height: 420px;
    overflow-y: auto;
    margin: 4px 0 6px;
    padding: 9px 12px;
    border: 1px solid transparent;
    border-image: var(--frame-inset) 8 fill / 8px;
    font-size: 0.9rem;
    line-height: 1.55;
    font-style: italic;
    color: var(--ink-2);
    text-shadow: none;
}

/* ----- Where a day turns: the line goes dotted, and there is the moon ----- */

.thread-day {
    position: relative;
    display: flex;
    align-items: center;
    min-height: 1.9rem;
    padding: 4px 0;
}

.thread-day::before {
    content: "";
    position: absolute;
    left: calc(var(--rail-x) - var(--gutter));
    top: calc(-1 * var(--gap));
    bottom: calc(-1 * var(--gap));
    width: 1px;
    background: repeating-linear-gradient(180deg, var(--rail) 0 2px, transparent 2px 6px);
}

.thread-day:first-child::before {
    top: 50%;
}

/* An inlaid disc: the lit part pale enamel, the rest the dark of the moon. */
.thread-day .moon {
    position: absolute;
    left: calc(var(--rail-x) - var(--gutter) + 0.5px);
    top: 50%;
    width: 23px;
    height: 23px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: rgb(var(--ground-rgb));
    filter: drop-shadow(var(--cast)) drop-shadow(0 0 5px rgba(232, 236, 250, calc(var(--glow-a) * 0.5)));
}

.moon-dark { fill: #2a3154; }
.moon-lit { fill: #f3f1e6; }
.moon-rim { stroke: rgba(236, 240, 252, 0.6); stroke-width: 1; }

.thread-day-label {
    font-family: var(--serif);
    font-style: italic;
    font-weight: 600;
    font-size: 1.2rem;
    letter-spacing: 0.01em;
    color: var(--ink-2);
    text-shadow: 0 0 10px rgba(var(--lens-rgb), 0.9);
}

/* The date's own scale bar runs on to the margin, as a section's does. */
.thread-day::after {
    content: "";
    flex: 1;
    height: 7px;
    margin-left: 14px;
    background:
        linear-gradient(var(--hair-2), var(--hair-2)) right top / 1px 7px no-repeat,
        repeating-linear-gradient(90deg, var(--hair-2) 0 1px, transparent 1px 8px) left top / 49px 4px no-repeat,
        linear-gradient(var(--hair-2), var(--hair-2)) left 3px / 100% 1px no-repeat;
}

/* ----- The rest of what a thread can hold ----- */

/* Machinery in the thread (a restart): a small inset. */
.message.system {
    padding: 9px 12px;
    border: 1px solid transparent;
    border-image: var(--frame-inset) 8 fill / 8px;
}

/* Its border moves the box in by a pixel; the rail stays where it was. */
.messages > .message.system::before {
    left: calc(var(--rail-x) - var(--gutter) - 1px);
}

.message.system > .message-header {
    margin-bottom: 4px;
    font: var(--caps-small);
    font-variant-numeric: lining-nums;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-2);
}

.message.system > .message-content {
    max-height: 150px;
    overflow-y: auto;
    font-size: 0.88rem;
    color: var(--ink-2);
    text-shadow: none;
}

.send-error .message-content { color: var(--bad); }

/* What a first visitor reads: the warm star, and the way in. */
.welcome-message {
    position: relative;
    max-width: 30rem;
    margin: 22px 0;
}

.welcome-message::before {
    content: "";
    position: absolute;
    left: calc(var(--rail-x) - var(--gutter) + 0.5px);
    top: 1.35rem;
    width: 19px;
    height: 19px;
    transform: translate(-50%, -50%);
    background: var(--star) center / contain no-repeat;
    filter: drop-shadow(var(--cast)) drop-shadow(0 0 6px rgba(var(--gilt-rgb), var(--glow-a)));
}

.welcome-message::after {
    content: "";
    position: absolute;
    left: calc(var(--rail-x) - var(--gutter));
    top: 1.35rem;
    bottom: -60vh;
    width: 1px;
    background: linear-gradient(180deg, var(--rail), transparent);
}

.welcome-message h2 {
    margin-bottom: 8px;
    font-family: var(--serif);
    font-style: italic;
    font-weight: 600;
    font-size: 2rem;
    line-height: 1.3;
    color: var(--ink);
}

.welcome-message p {
    font-size: var(--fs);
    line-height: var(--lh);
    color: var(--ink-2);
    text-shadow: 0 0 10px rgba(var(--lens-rgb), 0.9);
}

.welcome-message .key { margin-top: 16px; }

/* ===== The composer: the line ends in an empty ring, your turn ===== */

.chat-form {
    position: relative;
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 8px;
    padding: 4px var(--pad-right) 20px var(--gutter);
    /* the same smoke as behind the thread: the status line stands here bare */
    background: linear-gradient(90deg,
        transparent calc(var(--gutter) - 26px),
        rgba(var(--lens-rgb), var(--smoke-a)) calc(var(--gutter) - 2px),
        rgba(var(--lens-rgb), var(--smoke-a)) calc(100% - var(--smoke-end)),
        transparent calc(100% - var(--smoke-end) + 110px));
}

.chat-form::before {
    content: "";
    position: absolute;
    left: var(--rail-x);
    top: 0;
    bottom: 43px;
    width: 1px;
    background: linear-gradient(180deg, var(--rail), rgba(var(--steel-rgb), 0.6));
    box-shadow: -1px 0 0 var(--rail-shade);
    pointer-events: none;
}

.chat-form::after {
    content: "";
    position: absolute;
    left: calc(var(--rail-x) + 0.5px);
    bottom: 43px;
    width: 13px;
    height: 13px;
    transform: translate(-50%, 50%);
    border-radius: 50%;
    /* the ring a person's words hang from, with nobody's words on it yet */
    border: 1.5px solid rgba(var(--steel-rgb), 0.7);
    background: rgb(var(--ground-rgb));
    box-shadow: var(--cast);
    pointer-events: none;
}

.chat-form:focus-within::after {
    border-width: 2px;
    border-color: #dbe9ff #a4c8ff #7ea9ea #bcd7ff;
    box-shadow: var(--cast), inset 0 1px 1.5px rgba(0, 0, 14, 0.7), 0 0 10px rgba(var(--steel-rgb), 0.6);
}

/* Where you write: an inset in the glass, a scale of ticks along its foot
   like the neatline's. The scale turns to brass while you are writing. */
.composer-inset {
    position: relative;
    flex: 1;
    display: flex;
    min-width: 0;
}

.chat-form textarea {
    --foot: rgba(var(--steel-rgb), 0.62);
    flex: 1;
    min-width: 0;
    /* One line is exactly the send key's 46px: 1px border, 10px padding, a
       24px line. The line is in whole pixels and absolute, so the placeholder
       (Cormorant, larger) inherits the same line box as typed text, and the
       box is the same height empty, with its placeholder, and with a line in
       it. app.js grows it to max-height; it scrolls only past that. */
    min-height: 46px;
    max-height: 150px;
    padding: 10px 14px;
    overflow-y: hidden;
    resize: none;
    border: 1px solid transparent;
    border-image: var(--frame-inset-steel) 8 fill / 8px;
    background:
        repeating-linear-gradient(90deg, var(--foot) 0 1px, transparent 1px 80px) 8px 100% / calc(100% - 16px) 7px no-repeat,
        repeating-linear-gradient(90deg, var(--foot) 0 1px, transparent 1px 16px) 8px 100% / calc(100% - 16px) 4px no-repeat;
    color: var(--ink);
    font-size: 1rem; /* 16px: below it iOS zooms the page on focus */
    line-height: 1.5rem;
}

.chat-form textarea:focus {
    --foot: rgba(var(--gilt-rgb), 0.95);
    outline: none;
    border-image-source: var(--frame-inset-focus);
}

.chat-form textarea::placeholder {
    font-family: var(--serif);
    font-style: italic;
    font-weight: 600;
    font-size: 1.16rem;
    line-height: inherit; /* the textarea's 24px, not 1.45 of this larger size */
    /* one line, always: on a narrow phone it would wrap and grow the box */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--ink-3);
    opacity: 1;
}

/* Attach is a paperclip, and it is where a paperclip goes: on the edge of
   the sheet you are writing on, standing a little proud of it. Brass wire,
   lit from the upper left. The words leave it room. */
.composer-inset:has(.attach-btn:not([hidden])) textarea { padding-right: 52px; }

.chat-form .attach-btn {
    position: absolute;
    top: -14px;
    right: 3px;
    width: 46px;
    height: 60px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--gilt);
    cursor: pointer;
    transition: transform 0.12s ease, filter 0.15s ease;
}

.chat-form .attach-btn .icon {
    display: block;
    width: 46px;
    height: 46px;
    margin: 0 auto;
    stroke-width: 1.5;
    filter: drop-shadow(0.5px 1.5px 0.7px rgba(0, 0, 14, 0.9));
}

.chat-form .attach-btn .glint {
    stroke: var(--gilt-hi);
    stroke-width: 0.42;
    transform: translate(-0.3px, -0.3px);
}

.chat-form .attach-btn:hover {
    filter: brightness(1.15);
    transform: translateY(-2px);
}

.chat-form .attach-btn:active { transform: translateY(0); }

/* Send is the brass key, and what is engraved on it is an arrow: this goes.
   Where there is room the key says so in a word as well. (No pins: the arrow
   is what stands on this one.) */
.chat-form .send-btn {
    flex: none;
    align-self: flex-end;
    gap: 10px;
    min-width: 46px;
    min-height: 46px;
    padding: 0 16px 0 20px;
    font-size: 0.92rem;
}

.chat-form .send-btn::before,
.chat-form .send-btn::after { content: none; }

.send-glyph { display: flex; }

/* (cut into the brass: dark, its far edge catching the light) */
.chat-form .send-btn .icon {
    width: 26px;
    height: 26px;
    stroke-width: 1.8;
    filter: drop-shadow(0.5px 0.7px 0 rgba(255, 243, 207, 0.7));
}

html[data-palette="night"] .chat-form .send-btn {
    filter: drop-shadow(0 2px 3px rgba(0, 0, 10, 0.45)) drop-shadow(0 0 9px rgba(var(--gilt-rgb), 0.35));
}

/* While I am answering it waits: the key is pewter and the arrow is only
   ruled on it, not cut. */
.chat-form .send-btn:disabled,
html[data-palette="night"] .chat-form .send-btn:disabled {
    opacity: 1;
    border-image-source: var(--frame-key);
    color: var(--ink-3);
    filter: none;
}

.chat-form .send-btn:disabled .icon {
    stroke-width: 1.3;
    filter: none;
}

.chat-form .send-btn:disabled .icon path:first-child { fill: none; }

/* A narrow pane: the arrow alone, on a square key. */
@media (max-width: 767px) {
    .chat-form .send-btn {
        width: 48px;
        padding: 0;
    }

    .chat-form .send-btn .btn-text { display: none; }
    .chat-form .send-btn .icon { width: 30px; height: 30px; }
}

/* What the connection is doing, when it is worth saying. */
.chat-form .status {
    width: 100%;
    font-family: var(--serif);
    font-style: italic;
    font-weight: 600;
    font-size: 1.04rem;
    line-height: 1.2;
    letter-spacing: 0.02em;
    color: var(--ink-3);
}

.status.connected { display: none; }
.chat-form .status.busy { color: var(--gilt); }
.chat-form .status.error { color: var(--bad); }

/* Jobs running in the background, and files waiting to go: slips. */
.bg-processes,
.attachments {
    width: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.bg-processes:empty,
.attachments:empty {
    display: none;
}

.message .attachments { margin-top: 6px; }

.bg-process-pill,
.attach-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    max-width: 100%;
    min-height: 26px;
    padding: 0 14px 0 10px;
    border: 1px solid transparent;
    border-image: var(--frame-slip) 8 fill / 8px;
    color: var(--ink-2);
    font-size: 0.75rem;
    line-height: 1.2;
    letter-spacing: 0.04em;
    animation: tag-arrive 0.3s ease;
}

@keyframes tag-arrive {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: translateY(0); }
}

/* A small body in orbit: it is still going. */
.bg-proc-spinner {
    width: 9px;
    height: 9px;
    flex-shrink: 0;
    border-radius: 50%;
    border: 1.5px solid var(--gilt);
    border-top-color: transparent;
    animation: orbit 1s linear infinite;
}

.attach-chip-name {
    max-width: 18ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.attach-chip-remove {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    margin-right: -8px;
    border: 0;
    background: none;
    color: var(--ink-2);
    cursor: pointer;
}

.attach-chip-remove .icon { width: 13px; height: 13px; stroke-width: 2; }
.attach-chip-remove:hover { color: var(--ink); }

.attach-chip.is-error {
    border-image-source: var(--frame-slip-bad);
    color: var(--bad);
}

/* Dropping a file anywhere over the conversation works; this says so. */
.chat-container.is-drop-target {
    outline: 2px dashed var(--gilt);
    outline-offset: -8px;
}

/* ===== Phone and narrow panes ===== */

@media (max-width: 767px) {
    .messages { padding-top: 20px; }

    .chat-form { padding-bottom: 12px; }

    .chat-form::before,
    .chat-form::after { bottom: 35px; }

    .prose pre { font-size: 0.8rem; }

    .job-record .message-time { display: none; }

    /* The gutter is narrow here: a smaller drawing, wholly on screen. */
    .message-status-icon,
    .message-status .constellation-mini {
        width: 40px;
        height: 28px;
    }

    .message-status-text { padding-left: 4px; }
}

.chat-sidebar .message-status-icon,
.chat-sidebar .message-status .constellation-mini {
    width: 40px;
    height: 28px;
}

.chat-sidebar .message-status-text { padding-left: 4px; }

@media (pointer: coarse) {
    .job-record summary {
        min-height: var(--touch);
        align-items: center;
    }

    .messages > .message.job-record::after { top: 22px; }

    /* (a chip in the composer has a button on it; one on a sent message is
       only a tag) */
    .chat-form .attach-chip { min-height: var(--touch); }

    .attach-chip-remove {
        width: var(--touch);
        height: var(--touch);
        margin-right: -10px;
    }
}
