
:root { color-scheme: dark; }

/* `--ht-rule` is a *width* — 1px — not a border shorthand. `border: var(--ht-rule)` leaves
   border-style at its initial `none` and draws nothing at all, silently, which is how four
   borders in this file spent their whole lives invisible — including the one between two
   rows of the print queue, which is why five jobs read as one block. Every border here names
   its style and its colour. */

/* A class that sets `display` outbids the user agent's `[hidden] { display: none }`, which
   silently defeats every `hidden` attribute in the app — the "nothing matches" line showed
   through a full table, and the search button never went away after hydration. */
[hidden] { display: none !important; }

body {
  background: var(--ht-bg);
  color: var(--ht-text);
  font-family: var(--ht-font-ui);
  margin: 0;
}

.am-main {
  max-width: 78rem;
  margin-inline: auto;
  padding: var(--ht-space-5);
  display: grid;
  gap: var(--ht-space-6);
  align-content: start;
}

.am-bar {
  display: flex; align-items: center; gap: var(--ht-space-3);
  padding-block-end: var(--ht-space-3);
  border-block-end: var(--ht-rule) solid var(--ht-border);
}
.am-brand {
  display: inline-flex; align-items: center; gap: var(--ht-space-2);
  font-family: var(--ht-font-mono);
  font-size: var(--ht-text-2xs);
  letter-spacing: var(--ht-tracking-label);
  text-transform: var(--ht-case-label);
  color: var(--ht-accent);
  text-decoration: none;
}
/* The tab icon again, drawn at the size the name beside it is set in. It takes the accent
   from `.am-brand` through `currentColor`, so a faction switch moves it too. */
.am-mark { inline-size: 1.15rem; block-size: 1.15rem; flex: none; }

.am-eyebrow {
  font-family: var(--ht-font-mono); font-size: var(--ht-text-2xs);
  letter-spacing: var(--ht-tracking-label); text-transform: var(--ht-case-label);
  color: var(--ht-text-muted); margin: 0;
}
.am-caption {
  font-family: var(--ht-font-mono); font-size: var(--ht-text-2xs);
  letter-spacing: var(--ht-tracking-label); text-transform: var(--ht-case-label);
  color: var(--ht-text-muted); margin: var(--ht-space-4) 0 var(--ht-space-2);
}

/* Adding is the one thing that must never feel like a form, so it gets the top of the
   page and the widest control on it. */
.am-add { display: grid; gap: var(--ht-space-4); }
/* Wraps: at 320px an item's two footer buttons would otherwise overflow the card. */
.am-add-row { display: flex; gap: var(--ht-space-2); align-items: center; flex-wrap: wrap; }
/* Basis 0, not auto. `.ht-input` is `inline-size: 100%`, which an `auto` basis resolves to
   — so the field claimed the whole line and pushed its own button onto the next one, on
   every row built this way. The floor is what makes the wrap deliberate again: the button
   drops below only once the field would be too narrow to type in. */
.am-add-row .ht-input { flex: 1 1 0; min-inline-size: 10rem; font-size: var(--ht-text-lg); }
.am-add-row .ht-select { flex: 0 0 auto; }
.am-add-form { display: grid; gap: var(--ht-space-2); }

.am-just-added ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--ht-space-1); }
.am-just-added li {
  display: flex; justify-content: space-between; gap: var(--ht-space-3);
  font-size: var(--ht-text-sm);
}
.am-just-added a { color: inherit; }

.am-code, code.am-code {
  font-family: var(--ht-font-mono); font-size: var(--ht-text-2xs);
  color: var(--ht-text-muted); letter-spacing: .04em;
}

.am-stats { display: flex; flex-wrap: wrap; gap: var(--ht-space-8); align-items: start; }
.am-totals { display: flex; flex-wrap: wrap; gap: var(--ht-space-8); }

/* `inline-size: 100%` as well as the max and the auto margins. `.am-main` is a grid, and a
   grid item with `margin-inline: auto` is not stretched to its column — auto margins take
   the free space instead — so a card whose content happened to be narrower than the page
   shrank to fit and floated centred, half the width of the card above it. */
.am-item { inline-size: 100%; max-width: 44rem; margin-inline: auto; }

/* handterm's status list is built for short instrument readings — "OK", "NOMINAL" — so its
   values are uppercased and never wrap. An item's values are neither: a serial's case is
   data, and a manufacturer and model is long enough to run off a phone. Both would otherwise fail
   silently, clipping mid-word with no ellipsis to say so. */
.am-item .ht-status-row dd {
  text-transform: none;
  white-space: normal;
  overflow-wrap: anywhere;
  text-align: end;
}

/* A chip in a dense table row is a data label, not a touch target; handterm sizes it for a
   thumb, which makes tagged rows taller than untagged ones. */
.am-tags .ht-chip { min-block-size: 0; }
/* The one control on the page a stranger sees. It gets the palette and a thumb-sized
   target, not the browser's default violet underline at 13px. */
.am-found { margin-block-start: var(--ht-space-5); }
.am-found a {
  display: inline-flex; align-items: center; min-block-size: var(--ht-hit);
  padding-inline: var(--ht-space-4);
  color: var(--ht-accent-contrast); background: var(--ht-accent);
  text-decoration: none; font-weight: 600;
  corner-shape: var(--ht-corner-shape); border-radius: var(--ht-corner-sm);
}
.am-found a:hover { background: var(--ht-accent-hover); }
.am-public-code {
  font-family: var(--ht-font-mono); font-size: var(--ht-text-2xs);
  color: var(--ht-text-muted); letter-spacing: .08em;
}
.am-error { color: var(--ht-danger); }
.am-ok { color: var(--ht-ok); }

.am-empty {
  display: grid; gap: var(--ht-space-4); justify-items: start;
  padding-block: var(--ht-space-12); color: var(--ht-text-muted);
}
.am-empty h1 { color: var(--ht-text); margin: 0; }

/* The label preview is a picture of tape, so it keeps tape's white ground in every
   faction and every scheme rather than inverting with the page. */
.am-tape {
  display: block; background: #fff; padding: 6px;
  border: var(--ht-rule-strong) solid var(--ht-border-strong);
}
.am-tape img { display: block; inline-size: 100%; height: auto; image-rendering: pixelated; }

.ht-num { font-variant-numeric: tabular-nums; text-align: end; }

/* Not `.ht-scroller`: that is handterm's horizontal rail — `grid-auto-flow: column` with
   `max-content` tracks — which sizes a table to its contents and leaves the page half
   empty. A wide table wants a plain overflow container. */
.am-tablewrap { overflow-x: auto; }

/* On a phone the table is nearly useless sideways-scrolled: what matters is what a thing is,
   what it cost and how it is tagged. The rest is desk work. */
@media (max-width: 48rem) {
  .ht-table :is(th, td):nth-child(2),
  .ht-table :is(th, td):nth-child(3),
  .ht-table :is(th, td):nth-child(6),
  .ht-table :is(th, td):nth-child(7) { display: none; }
}

/* Search and tags: a filter bar reads as a control strip, not as a form to fill in. */
.am-nomatch { padding-block: var(--ht-space-4); }
.am-filter { display: flex; gap: var(--ht-space-2); align-items: center; flex-wrap: wrap; }
.am-filter .ht-input { flex: 1 1 18rem; min-inline-size: 0; }
.am-tagbar { gap: var(--ht-space-2); }
.am-tagbar .am-count { margin-inline-start: var(--ht-space-1); opacity: .7; }
/* The flex box goes on a child, never on the `td`. A table cell told to be a flex
   container stops being a table cell: it no longer stretches to the row's height, so its
   bottom border draws mid-row and an empty one looks like a stray rule. */
.am-tags { display: flex; gap: var(--ht-space-1); flex-wrap: wrap; }
.am-tags .ht-chip { font-size: var(--ht-text-2xs); }
.am-none { color: var(--ht-text-muted); }

/* The edit form asks three questions — what the thing is, what it cost, what to remember —
   and used to ask all of them in one undifferentiated column of ten identical boxes. The
   groups are what make it scannable; the gap between them is larger than the gap inside. */
.am-edit { display: grid; gap: var(--ht-space-8); }

.am-group {
  border: 0; margin: 0; padding: 0;
  display: grid; gap: var(--ht-space-4) var(--ht-space-3);
  /* Two columns where there is room for two, one where there is not — so a date stops being
     a ten-character value in a full-width box without anything being pinned to a guessed
     width. `wide` is for the values that are prose and want the whole line. */
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  /* Top, not stretched. The warranty cell is taller than its neighbour — it carries the
     one-click years underneath — and a stretched row grew the field beside it into a box
     half again the height of every other input. */
  align-items: start;
}
.am-group > legend { grid-column: 1 / -1; inline-size: 100%; }
.am-group > .ht-field[data-wide] { grid-column: 1 / -1; }
/* The rule is what makes a group read as a group. Without it the section heading was the
   same small muted text as the field labels under it, and the grouping had to be inferred
   from the gaps. */
.am-edit .am-caption {
  margin: 0 0 var(--ht-space-1);
  padding-block-end: var(--ht-space-2);
  border-block-end: var(--ht-rule) solid var(--ht-border);
  color: var(--ht-text);
}

/* A label names its field; it is not the thing to read. handterm sizes these for an
   instrument panel with two of them on screen, and at ten deep, uppercase and bold at the
   same size as the value, the column read as a ladder of shouting. Quieter and smaller, so
   what you typed is the figure and what it is called is the ground. */
.am-edit .ht-field-label {
  font-size: var(--ht-text-2xs);
  font-weight: 500;
  color: var(--ht-text-muted);
}
/* Full width like every other field. A wrapper that collapses to its content is how one
   row ends up ragged in a column of otherwise flush inputs. */
/* Basis 0 on both, for the reason `.am-add-row` carries: handterm sizes fields and selects
   `inline-size: 100%`, so an `auto` basis is the whole row. Here the currency select was
   also `flex-shrink: 0`, which held it at that full width and squeezed the amount down to a
   sliver too narrow to see a price in. */
/* The amount is the field; the currency is a qualifier on it. Sized that way round — the
   select was resolving its `inline-size: 100%` through an `auto` basis and taking two thirds
   of the row, which left the price in a box too narrow to read a four-figure sum in. */
.am-money { display: flex; gap: var(--ht-space-2); inline-size: 100%; }
.am-money .ht-input { flex: 1 1 auto; min-inline-size: 0; }
.am-money .ht-select { flex: 0 0 auto; inline-size: 6.5rem; min-inline-size: 0; }
/* The one-click dates belong to the field above them, so they sit tight under it rather
   than floating in the gap between two fields — which is where they read as neither. Given
   a chip's outline, because ghost buttons at the smallest size were invisible against the
   card until you knew to look. */
.am-offsets {
  display: flex; gap: var(--ht-space-2); flex-wrap: wrap;
  margin-block-start: var(--ht-space-2);
}
.am-offsets .ht-btn {
  font-size: var(--ht-text-2xs);
  --ht-btn-border: var(--ht-border);
  --ht-btn-fg: var(--ht-text-muted);
}
.am-offsets .ht-btn:hover { --ht-btn-fg: var(--ht-text); --ht-btn-border: var(--ht-border-accent); }
.am-edit textarea { resize: vertical; font-family: inherit; min-block-size: 5lh; }
.am-public {
  display: grid; gap: var(--ht-space-2);
  border: var(--ht-rule) solid var(--ht-border); padding: var(--ht-space-4);
  corner-shape: var(--ht-corner-shape); border-radius: var(--ht-corner-sm);
}
/* Left, like every other heading on the page. It was centring itself because a legend
   inherits nothing from the column it sits in. */
.am-public legend { padding-inline: var(--ht-space-2); text-align: start; }

/* Documents are a list of links, not instrument readings. The status list beside them
   right-aligns its values and is sized for "OK" — a document title is neither short nor a
   value, and set that way it wrapped ragged against the right edge and read as one more
   row of the record above it. */
.am-docs { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--ht-space-2); }
.am-doc {
  display: flex; gap: var(--ht-space-3); align-items: center; flex-wrap: wrap;
  font-size: var(--ht-text-sm);
}
/* A small basis so the title gives way first. With a wide one the badges and the button
   filled the line and Detach dropped to a line of its own, leaving a gap that reads as a
   missing row. */
.am-doc-title { flex: 1 1 8rem; min-inline-size: 0; overflow-wrap: anywhere; }
.am-doc-act { margin-inline-start: auto; flex: 0 0 auto; }

/* A row's own control, said as quietly as it can be while still being a control.
   `.ht-btn` is sized and bordered for a primary action, which gave "Detach" the same weight
   as the document it sat beside — and detaching is the least consequential thing on the
   page, since the document stays in paperless either way. Stripped to text, kept at a
   thumb's height, and only coloured on the way to being pressed. */
.am-quiet.ht-btn {
  --ht-btn-bg: transparent;
  --ht-btn-border: transparent;
  --ht-btn-fg: var(--ht-text-subtle, var(--ht-text-muted));
  padding-inline: var(--ht-space-1);
  font-size: var(--ht-text-2xs);
  font-weight: 500;
  opacity: .7;
}
.am-quiet.ht-btn:hover, .am-quiet.ht-btn:focus-visible {
  --ht-btn-bg: transparent;
  --ht-btn-fg: var(--ht-danger);
  opacity: 1;
  text-decoration: underline;
  text-underline-offset: .2em;
}
.am-doc .ht-badge { flex: 0 0 auto; }
/* These leave the application, and they were the only links in it still wearing the
   browser's violet. The marker says so without needing the colour to carry it. */
.am-doc-link { color: var(--ht-accent); text-underline-offset: .2em; }
.am-doc-link:hover { color: var(--ht-accent-hover); }
.am-ext { font-size: var(--ht-text-2xs); opacity: .8; margin-inline-start: .25em; }

/* Documents are edited beside the fields, not on the page a scanned label lands on. Its own
   card because these take effect on the press, where the fields above wait for Save. */
.am-documents .ht-card-title { font-size: var(--ht-text-base); }
/* The card body is a plain block, so the list and the control below it sat flush against
   each other — and a title long enough to wrap put a wrapped line directly under the
   button's edge. The gap is what says they are two things. */
.am-documents .ht-card-body {
  display: grid; gap: var(--ht-space-4); align-content: start;
}
/* The grid would stretch it edge to edge. Every other button in this app is the width of
   its own label; the folded-away panel it replaces is the thing that wants the full line. */
#am-attach-open { justify-self: start; }

/* Attaching a document is a detour from reading the page, so it stays folded away. */
.am-attach {
  border: var(--ht-rule) solid var(--ht-border);
  padding: var(--ht-space-3) var(--ht-space-4);
}
.am-attach summary { cursor: pointer; font-size: var(--ht-text-sm); }
.am-attach-body { display: grid; gap: var(--ht-space-4); padding-block-start: var(--ht-space-4); }
.am-attach-panes { display: grid; gap: var(--ht-space-4); }
.am-attach-pane { display: grid; gap: var(--ht-space-3); }
/* The dialog supplies its own padding, and the details' fold no longer exists inside it. */
.am-attach-dialog .am-attach-body { padding-block-start: 0; }
/* Only the results scroll. The search box and the tab strip stay put, so a long list never
   pushes the field that produced it off the top of the dialog. */
.am-attach-dialog .am-hits { max-block-size: 45dvh; overflow-y: auto; }
.am-doc-status { margin: 0; font-size: var(--ht-text-sm); min-block-size: 1lh; }
.am-hits { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--ht-space-3); }
/* Each result carries its own controls, so the rule between them is what says which
   dropdown belongs to which title. A hairline was not enough to answer that. */
.am-hit {
  display: flex; gap: var(--ht-space-3); align-items: center; flex-wrap: wrap;
  font-size: var(--ht-text-sm); padding-block-end: var(--ht-space-3);
  border-block-end: var(--ht-rule-strong) solid var(--ht-border);
}
.am-hits .am-hit:last-child { border-block-end: 0; padding-block-end: 0; }
.am-hit-title { flex: 1 1 12rem; min-inline-size: 0; overflow-wrap: anywhere; }
/* `flex: 0 0 auto` on both, because a button squeezed below its label does not ellipsise:
   handterm centres the text, so it overflows equally at both ends and "Change role" is
   clipped to "hange rol". These controls wrap to their own line instead of narrowing. */
.am-hit-form {
  display: flex; gap: var(--ht-space-2); align-items: center;
  margin-inline-start: auto; flex: 0 0 auto;
}
.am-hit-form .ht-btn, .am-doc-act .ht-btn { flex: 0 0 auto; white-space: nowrap; }
.am-hit .ht-badge { flex: 0 0 auto; }
.am-upload { display: grid; gap: var(--ht-space-2); }
.am-upload :disabled { opacity: .55; cursor: not-allowed; }

/* A document paperless has not finished reading. Present, named, and visibly not yet a
   link — the row is the only thing on the page that says an upload is still in flight, so
   it has to read as unfinished rather than as broken. */
.am-doc-waiting { color: var(--ht-text-muted); }
.am-doc-reading {
  margin-inline-start: auto; flex: 0 0 auto;
  font-family: var(--ht-font-mono); font-size: var(--ht-text-2xs);
  letter-spacing: var(--ht-tracking-label); text-transform: var(--ht-case-label);
  color: var(--ht-text-muted);
}
@media not (prefers-reduced-motion: reduce) {
  .am-doc-reading { animation: am-breathe 2s var(--ht-ease-out) infinite; }
}
@keyframes am-breathe { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

/* An upload that ended without a link. Not greyed like one still being read: it is finished
   and it is waiting to be read, so it keeps full contrast and carries its reason on a line
   of its own rather than crammed in beside the title. */
.am-doc-settled { align-items: start; }
.am-doc-note {
  display: block; font-size: var(--ht-text-2xs); color: var(--ht-text-muted);
  margin-block-start: var(--ht-space-1); text-wrap: pretty;
}
.am-public-manual { margin-block-start: var(--ht-space-4); }
table a { color: inherit; }

/* The print room. Two lists of one-line facts, so both are rows that wrap rather than
   tables: on a phone this is read standing next to the printer. */
.am-daemons, .am-queue { list-style: none; margin: 0; padding: 0; display: grid; }
/* A row, not a paragraph of inline boxes. As a wrapping flex with the status pushed right
   by an auto margin it failed at both ends: on a phone the controls dropped to an
   unindented line of their own, so a job's own button sat under the next job's code, and
   on a desktop the same margin opened most of a thousand pixels between a label and its
   own status. Three columns — what it is, what it says, what you can do — keeps those
   together at every width, and the row keeps one height. */
.am-daemon, .am-job {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  /* Top, not centre: a row with an error runs to three lines, and a centred badge floated
     to the middle of them — level with the error rather than with the name it labels. */
  align-items: start;
  gap: var(--ht-space-1) var(--ht-space-3);
  padding-block: var(--ht-space-3);
  border-block-end: var(--ht-rule) solid var(--ht-border);
}
.am-row-what { min-inline-size: 0; display: grid; gap: var(--ht-space-1); }
.am-row-end {
  display: flex; align-items: center; gap: var(--ht-space-3);
  justify-content: flex-end; flex-wrap: wrap;
}
/* After the rule it narrows, not before it: same specificity, so source order is the whole
   of the argument. On a phone there is no room for three columns, so the controls take a
   line under the row rather than squeezing the name to nothing — left, under the badge,
   because the rule beneath the row is now what says where the row ends. */
@media (max-width: 34rem) {
  .am-daemon, .am-job { grid-template-columns: auto minmax(0, 1fr); }
  .am-row-end { grid-column: 1 / -1; justify-content: flex-start; }
}
.am-row-act { flex: 0 0 auto; }
.am-daemon:last-child, .am-job:last-child { border-block-end: 0; }
/* The badge caps the first line, so it lines up with the name however tall the row gets. */
.am-daemon > .ht-badge, .am-job > .ht-badge { flex: 0 0 auto; }
.am-daemon-name { font-family: var(--ht-font-mono); }
.am-daemon-detail, .am-job-name { color: var(--ht-text-muted); min-inline-size: 0; overflow-wrap: anywhere; }
/* Pushed to the end together, so the row reads left to right as name, then state, then the
   one control — and the controls line up down the list. */
.am-daemon-seen, .am-job-where {
  color: var(--ht-text-muted); font-size: var(--ht-text-2xs); white-space: nowrap;
}
/* Inside the row's middle column now, under the name it belongs to, rather than a
   full-width break that split the row in two. */
.am-job-error {
  font-size: var(--ht-text-2xs); color: var(--ht-text-muted);
  display: grid; gap: 2px;
}
.am-job-code { font-family: var(--ht-font-mono); text-decoration: none; }

/* Picker and button on one line: choosing where and saying print is one decision. */
/* Each part of the print room is a card, like every other page in this application. It
   was the one page built out of bare sections on the page background, with nothing saying
   where you were. */
.am-printroom .ht-card-title { font-size: var(--ht-text-base); }
.am-printroom-send { display: grid; gap: var(--ht-space-2); justify-items: start; }
.am-printroom-send .ht-field-description, .am-printroom-send .am-none { margin: 0; }
.am-strip-count { display: grid; gap: var(--ht-space-1); }
/* Sits beside the button rather than under it: without a machine ready the press still
   does something, and the sentence explaining that is not an error. */
.am-print-note { font-size: var(--ht-text-2xs); }

/* A row of labelled controls. The gap is the space between two *fields*, not two words —
   at the tighter step "How many" and "Send to" sat eight pixels apart and read as one
   heading over the wrong box. */
.am-print-form, .am-print-again {
  display: flex; gap: var(--ht-space-4); align-items: center; flex-wrap: wrap;
  margin-block-start: var(--ht-space-4);
}
.am-print-again { margin-block-start: 0; }
.am-strip-n { inline-size: 5rem; }
/* Each labelled control is one box in the row, and the picker is the one that grows. */
.am-strip-count { flex: 0 0 auto; }
.am-daemon-field { display: grid; gap: var(--ht-space-1); flex: 1 1 12rem; min-inline-size: 0; }
.am-print-form, .am-print-again { align-items: end; }
/* The footer stacks instead, so a long machine name never squeezes the button that sends to
   it — and the sentence explaining the count sits under both. */
.am-printroom-send .am-print-form { inline-size: 100%; }

/* Names a control for a screen reader where the layout has no room to name it on screen. */
.am-sr {
  position: absolute; inline-size: 1px; block-size: 1px;
  margin: -1px; padding: 0; overflow: hidden; white-space: nowrap;
  clip-path: inset(50%); border: 0;
}

:where(a, button, input, [tabindex]):focus-visible {
  outline: 2px solid var(--ht-focus-ring); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}
