/* Contract: contracts/web/rules.md
 * the assistant's line at the foot of the screen, and the bubble over it
 *
 * The tenth part, written after the split rather than carved out of it. THE
 * ORDER IS STILL THE CASCADE — this loads last of the ten and therefore wins
 * against all of them, which is exactly what the `--floor` overrides at the foot
 * of this file need and the only reason they are here rather than beside the
 * rules they adjust.
 *
 * There are deliberately no @layer rules, for the reason every other part of
 * this stylesheet says so: layering would reverse at least one pair that
 * currently resolves on specificity.
 */

/* ---------- the bar ----------
   In the flow, as a sibling of .workspace. body.app is a flex column and the
   workspace is flex: 1, so a footer with a height takes that height off the
   columns and the whole interface moves up by exactly as much as this is tall.
   No offsets are calculated anywhere and none can go stale, which is the same
   argument the shell makes for having no sticky furniture.

   AND IT IS PAINTED IN THE ACCENT, which is the half of this section that costs
   something, and it is a decision about what the bar IS rather than about how
   it looks. A footer the colour of the panel is furniture — the same grey as
   the sidebar and the filter row, read as one more strip of chrome. The accent
   says a person is standing there. It is the
   same colour the mark in the top bar uses and the same one src/config.mjs
   gives each deployment to be told apart by, so the bar belongs to the archive
   it is in rather than to a palette invented here.

   This is not a new assumption about contrast: `.btn` in web/css/03-grid.css
   has always been `background: var(--accent); color: #fff`, so every primary
   button in the application already stands on this ground. What is new is the
   AMOUNT of it. A button carries one word; this band carries a label, a
   placeholder, a pill, three ghosts and a count, and a colour that is merely
   survivable behind one word is tiring behind six things. So the accent gets a
   proper set of on-colours rather than a single `#fff`, declared once here and
   used by every rule below.

   THE INVERSION IS THE POINT AND IT IS EASY TO MISREAD. `--on-accent` is white
   in the LIGHT scheme and near-black in the DARK one, which is the opposite way
   round from `--ink`. That is not a mistake: the accent itself is dark in the
   light scheme and light in the dark one — that is how a single setting stays
   visible against two opposite backgrounds — so anything standing ON it has to
   invert with it. Read the accent, not the page, when changing these.

   The one case this does not cover is an operator who sets ARCHIVE_ACCENT to a
   single hex, which src/config.mjs allows: one colour used in both schemes
   means one of the two inversions is wrong. That file already documents the
   same hazard for the accent itself — "a colour picked against the dark shell
   can be unreadable on the light one" — and the answer is the same, which is to
   pick a pair. Nothing here can detect it: CSS has no way to ask how light a
   custom property turned out to be. */
.assistant {
  position: relative; z-index: 59;
  flex: none;

  /* Declared on the bar rather than in web/css/01-tokens.css because they mean
     nothing anywhere else — no other surface in the archive is painted in the
     accent at this size. Put in the tokens file they would be four more names
     to read past on every page, including the two that do not load this part. */
  --on-accent: light-dark(#ffffff, #2a100a);
  /* Text that is present without being the point: the name, the pill, the
     ghosts' labels.

     92% LOOKS LIKE A ROUNDING ERROR AWAY FROM PLAIN WHITE, AND THAT IS THE
     FINDING rather than a fudge. Pure white on the light scheme's accent
     measures 5.38:1 — the whole budget this ground has — so a dimmed tier
     underneath it has about one point of room before 12px text drops through
     4.5:1. Written at 72% first, the picker measured 3.66 and the name, pill
     and ghosts 4.4. `--ink-dim` gets to be genuinely soft because it stands on
     a panel almost its own colour; nothing on a saturated ground does.

     So the quiet tier here is nearly-white, and the hierarchy that alpha can no
     longer carry is carried by weight and by whether a control is outlined at
     all — see the picker below. That is the better instrument anyway: fading
     text toward its background is a way of making it harder to read, and on
     this ground it is ONLY that. */
  --on-accent-soft: color-mix(in oklab, var(--on-accent) 92%, transparent);
  /* Outlines of controls, which are held to 3:1 in their own right — a ghost
     button whose border nobody can find is a button nobody can find. At 42%
     this measured 2.15 and looked like a smudge; 60% still came up 2.92, which
     is the kind of miss that only a measurement finds. */
  --on-accent-line: color-mix(in oklab, var(--on-accent) 64%, transparent);
  /* A wash for the things that want to sit slightly proud of the accent without
     becoming a second box: the about-pill, the empty thumbnail, and a ghost
     under the pointer. Not held to any ratio, because nothing is ever read
     against it — it is a shape, and what sits on it is read against the ground
     it composites into. */
  --on-accent-fill: color-mix(in oklab, var(--on-accent) 16%, transparent);

  background: var(--accent);
  /* No border. The panel-coloured bar needed a line to separate it from a
     workspace the same colour; the accent separates itself, and a `--line`
     border drawn on top of it is a grey smear nobody asked for. */
}

/* The two conditions that stand it down, and both are classes somebody else
   already sets — there is no third boolean for this feature to keep in step.
   `in-requests` is the workspace, which has a compose box of its own at the foot
   of the open thread; two boxes on one screen meaning two different things is the
   duplication web/requests-view.js exists to have killed once already.
   `choosing` is the phone's selection mode, where the action bar owns the bottom
   of the screen outright and the header has already gone the same way.

   Nothing has to tell web/assistant.js about either. Going display: none makes the
   footer measure as zero, its ResizeObserver fires, and `--floor` follows on its
   own — which is the whole reason that value is measured rather than declared. */
body.in-requests .assistant,
body.choosing .assistant { display: none; }

/* The ground colour is set on the footer so the band reaches the full width of
   the window, but the TEXT colour is set here, one level in, and the difference
   matters. The bubble is a child of the footer as well — it is positioned above
   it, over the workspace, on `var(--panel)` — so a `color` on .assistant would
   be inherited by a question sitting on a white panel in the light scheme, and
   white on white is the whole of what somebody would see. Anything painted for
   the accent goes inside .assistant-line, which is the only part actually
   standing on it. */
.assistant-line {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 20px calc(10px + env(safe-area-inset-bottom));
  color: var(--on-accent);
}

/* The same shape as the archive's own mark in the top bar, small, so the line
   reads as belonging to somebody rather than being another filter row. It is
   decoration and says so in the markup: the name itself is on the bubble, where
   there is room for it to be read.

   It used to be a gradient FROM the accent, which was how it stood out on a
   panel-coloured bar and is exactly why it cannot stay: an accent mark on an
   accent ground is a mark nobody can see. Solid on-accent instead, which is the
   same relationship the other way up. */
.assistant-mark {
  flex: none; width: 12px; height: 12px; border-radius: 4px;
  background: var(--on-accent);
}
/* The name, quietly, so the line has somebody on it even once the placeholder
   has been typed over. First thing to go when the window gets narrow: the
   placeholder says it again, and on a phone the box is worth more than the
   introduction. */
.assistant-who {
  flex: none; margin-left: -3px;
  font-size: 12.5px; font-weight: 600; color: var(--on-accent-soft);
}
@media (max-width: 900px) { .assistant-who { display: none; } }

/* What the next sentence is about. Capped, because it is a label on a control
   and not the control: a long filename must not be able to squeeze the box
   somebody is trying to type in down to nothing. */
.assistant-about {
  flex: none; display: flex; align-items: center; gap: 7px;
  max-width: min(38%, 420px);
  padding: 4px 9px 4px 5px;
  /* The wash alone, with no border. On the panel this pill needed a `--line`
     edge to be a shape at all; on the accent the wash is already a shape, and
     an outline as strong as the ghost buttons' would make a label look like a
     control somebody can press. */
  background: var(--on-accent-fill); border: 1px solid transparent;
  border-radius: 999px;
  font-size: 12px; color: var(--on-accent-soft);
}
.assistant-about[hidden] { display: none; }
.assistant-thumb { flex: none; display: block; width: 22px; height: 22px; }
.assistant-thumb img {
  width: 22px; height: 22px; object-fit: cover;
  /* The ground behind a thumbnail that has not loaded, and it is the wash
     rather than `--panel` now: a panel-coloured disc on the accent is a hole in
     the bar, which is a strange thing for a picture to leave behind. */
  border-radius: 50%; background: var(--on-accent-fill);
}
.assistant-quote { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.assistant-count { flex: none; color: var(--on-accent); font-variant-numeric: tabular-nums; }
/* Pictures that will not travel. It used to be said in the accent, because on a
   panel-coloured bar the accent was the loudest thing available and this is the
   one item on the line that contradicts what the person just did. The accent is
   now the ground, so the emphasis has to come from somewhere else: full-strength
   on-accent against the soft grey-white everything around it is written in,
   plus the weight. Same job, opposite direction. */
.assistant-over { flex: none; color: var(--on-accent); font-weight: 600; }

.assistant-say {
  flex: 1; min-width: 0;
  /* min-height rather than a height, so the box is the same size as the buttons
     beside it when empty and grows with `field-sizing` where that is supported.
     Where it is not, rows="1" plus the max-height below means a long paragraph
     scrolls inside the box instead of pushing the grid up — a degradation
     nobody would notice, on the rare request that is longer than a sentence. */
  field-sizing: content;
  min-height: var(--control-h); max-height: 120px;
  padding: 9px 12px; resize: none;
  /* `--panel` rather than the wash the pill uses, and it is the one place on
     this bar that keeps the page's own colours. A box somebody types a sentence
     into wants to be a hole in the accent rather than a shape drawn on it: it
     is the only control here that holds their words instead of ours, and the
     contrast is what says so. It reads the same way in both schemes for the
     same reason the ground does — white well on a red band, near-black well on
     a salmon one. */
  background: var(--panel); color: var(--ink);
  border: 1px solid transparent; border-radius: var(--radius);
  font: inherit; font-size: 14px; line-height: 1.4; outline: none;
}
/* Focus was the accent, which is now the thing the box is standing on. The ring
   is the ground's own on-colour instead, and it is a ring rather than a border
   alone because one pixel of near-white against a red band is not a state
   change anybody notices. */
.assistant-say:focus {
  border-color: var(--on-accent);
  box-shadow: 0 0 0 2px var(--on-accent-line);
}
/* Dimmed less than it was, and the reason is the same one the soft token
   carries: at .6 the box blends toward the accent and takes the placeholder
   with it, and that placeholder is not decoration — it is the sentence
   explaining WHY the box will not take words ("{who} is making that now"). A
   disabled control may look inert; the only line of text saying what is going
   on may not. */
.assistant-say:disabled { opacity: .82; }

.assistant-line .ghost, .assistant-line .btn { flex: none; height: var(--control-h); }

/* The three ghosts. `.ghost` is written for a panel — dim ink, a `--line`
   border — and both of those disappear here, so all three are restated against
   the ground. Still ghosts: no fill until the pointer arrives. */
.assistant-line .ghost {
  color: var(--on-accent-soft); border-color: var(--on-accent-line);
}
.assistant-line .ghost:hover {
  color: var(--on-accent); border-color: var(--on-accent);
  background: var(--on-accent-fill);
}
/* The picker is the least of the four and reads as such. It is offered for the
   case the grid cannot answer — a reference that is not what you are currently
   looking at — and most sentences never need it.

   "Least" used to be said by dimming the words, which on this ground means
   3.66:1 and is not a way of saying anything — it is just the label being
   harder to read than the ones either side of it. Said by dropping the outline
   instead: a bare label among three bordered controls is plainly the quietest
   thing on the line, at exactly the same contrast as its neighbours. The border
   comes back under the pointer, which is the moment it stops being quiet. */
.assistant-line .assistant-pick { border-color: transparent; }
.assistant-line .assistant-pick:hover { border-color: var(--on-accent-line); }

/* Send, inverted. `.btn` is accent-on-white everywhere else in the archive,
   which on this bar is the button painted in the colour of the thing behind it
   — the single worst casualty of the change and the reason a colour swap here
   could not be one declaration. Turning it over keeps the relationship exactly:
   the strongest contrast available on the line, still reading as the one filled
   control among three outlined ones. */
.assistant-line .btn {
  background: var(--on-accent); color: var(--accent);
}
.assistant-line .btn:hover:not([disabled]) {
  background: color-mix(in oklab, var(--on-accent) 88%, var(--accent));
}
/* `.btn[disabled]` is opacity .45 everywhere else, which on a panel leaves a
   pale grey button and on the accent leaves 2.2:1 — the button all but gone.
   That would be tolerable for a control nobody needs to read, and this is not
   one: disabled is exactly when it says "Reading…", which is the only thing on
   screen telling somebody their sentence went. Dimmed enough to be plainly
   unpressable, not enough to be unreadable. */
.assistant-line .btn[disabled] { opacity: .72; }

/* ---------- the bubble ----------
   Out of the flow, so a question arriving does not shorten the grid underneath
   it. That is the difference between a notice and a layout change: the pictures
   somebody was looking at when they asked are still exactly where they were, and
   the answer to the question is very often one of them.

   Anchored to the bar rather than to the window, so it travels with it and needs
   to know nothing about how tall anything else is. Its height IS added to
   `--floor` from web/assistant.js, which is what keeps the selection bar and the
   toast above it instead of underneath. */
.assistant-bubble {
  position: absolute; bottom: calc(100% + 10px); left: 20px;
  width: min(620px, calc(100% - 40px)); z-index: 59;
}
.assistant-bubble[hidden] { display: none; }

.assistant-said {
  padding: 12px 14px;
  background: var(--panel); border: 1px solid var(--warn);
  /* The one square corner points at the bar it came from — the same grammar the
     thread log uses for a question, so a question read here and a question read
     in the workspace are recognisably the same thing said twice. */
  border-radius: 14px 14px 14px 3px;
  box-shadow: 0 12px 34px rgba(0, 0, 0, .32);
}
/* Nothing waiting on anybody: the studio has it, or it is a remark. Quiet
   border, because a bubble that shouts the same way whether or not it needs an
   answer stops meaning anything. */
.assistant-said.note { border-color: var(--line); }
.assistant-said.bad { border-color: var(--accent); }

/* Said, rather than left to the shape of the box. A question with no name on it
   is the software talking; this whole change is about it being somebody. */
.assistant-name {
  display: block; margin-bottom: 4px;
  font-size: 11px; font-weight: 650; letter-spacing: .02em;
  color: var(--accent);
}
.assistant-text { margin: 0; font-size: 13.5px; line-height: 1.5; }
.assistant-said.bad .assistant-text { color: var(--accent); }

.assistant-bubble-actions {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 10px;
}
.assistant-bubble-actions .ghost { height: 30px; padding: 0 11px; font-size: 12.5px; }
/* Pushed to the far end: dismissing is the one thing here that does not answer
   anything, and it should not sit in the run of buttons that do. */
.assistant-x {
  margin-left: auto; width: 24px; height: 24px; padding: 0; line-height: 1;
  border-radius: 50%; border: 1px solid var(--line);
  background: transparent; color: var(--ink-dim);
  font: inherit; font-size: 11px; cursor: pointer;
}
.assistant-x:hover { color: var(--ink); border-color: var(--ink-dim); }

/* ---------- the floor ----------
   Four things are pinned to the window with position: fixed, and a flex sibling
   cannot push any of them: the selection bar, the upload tray, the toast, and
   the phone's camera button. Each keeps the offset it already had and adds this,
   which web/assistant.js sets to the measured height of the bar plus whatever
   it currently has to say.

   Written as one variable rather than as four overrides in this file because the
   offsets themselves belong where they are — 18px in one place, 78 in another,
   94 on a phone — and a copy of each number here would be four more chances for
   the two to disagree. The rules that read it are edited in place, in the parts
   that own them.

   Every one of them writes `var(--floor, 0px)` rather than relying on a default
   declared here, and that is not belt-and-braces. login.html and share.html load
   parts one to nine and NOT this one, so a bare `var(--floor)` there would be an
   unresolved variable, which makes the whole calc() invalid and drops `bottom`
   back to auto — the toast and the phone's camera button would leave the corner
   entirely, on two pages that have nothing to do with this feature. The value is
   only ever set from web/assistant.js, as an inline property on the root element,
   which beats any stylesheet without needing to be declared in one. */
