/* ============================================================================
   Aucto Console — v2
   ----------------------------------------------------------------------------
   Three structural laws this stylesheet exists to enforce:

   1. THERE IS NO WIZARD. A record exists from the first keystroke and fills up.
      Progress is readiness, not a numbered track. No .step, no .stepper.
   2. THERE ARE NO TABS. One surface, one scroll, a rail that shows every
      section AND its count at the same time. No .tab.
   3. PROPERTIES ARE ROWS, NOT FIELDS IN BOXES. A value at rest is text. The
      box appears when you focus it and disappears when you leave.

   Plus: navigation is a left sidebar (the inverted L), never a top band; a
   border is earned — only what floats or what you can type into gets one.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 19px;
  letter-spacing: -0.006em;
  color: var(--text-primary);
  background: var(--bg-canvas);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-variant-numeric: tabular-nums;
}

button, input, select, textarea { font: inherit; letter-spacing: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
table { border-collapse: collapse; border-spacing: 0; }
svg { display: block; flex: 0 0 auto; }

:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 1px;
  border-radius: var(--radius-sm);
}

/* ---------------------------------------------------------------- scrollbars
   Quiet. A scrollbar is chrome, and chrome does not announce itself. */
.ax-scroll { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border-interactive) transparent; }
.ax-scroll::-webkit-scrollbar, .ax-tblwrap::-webkit-scrollbar { width: 9px; height: 9px; }

/* `hidden` MEANS HIDDEN, AND A LAYOUT MODE DOES NOT GET A VOTE.

   `[hidden] { display: none }` comes from the user agent, so ANY author rule
   carrying a `display` beats it -- and this system sets `display` on nearly
   everything it draws. The consequence is that `node.hidden = true` silently
   does nothing to a button, a flex row or a table row.

   Found twice before it was believed: a folded table re-showed the empty
   state the factory had hidden, drawing a 21px stripe of border on three
   screens; and a modal footer showed both of its states at once, so an
   `Accept` and a `Send counter` sat side by side. `!important` is the right
   weight here precisely because nothing should ever outrank it: an element
   the application has hidden is not a layout question. */
[hidden] { display: none !important; }

.ax-scroll::-webkit-scrollbar-thumb, .ax-tblwrap::-webkit-scrollbar-thumb { background: var(--border-interactive); border-radius: var(--radius-pill); border: 2px solid transparent; background-clip: content-box; }
.ax-scroll::-webkit-scrollbar-track, .ax-tblwrap::-webkit-scrollbar-track { background: transparent; }

/* ============================================================================
   1 · SHELL — the inverted L. Nav lives on the left, forever.
   The topbar and the orange impersonation band are both gone: the company you
   are acting as is a chip inside the nav, because that is where identity lives.
   ========================================================================== */


/* AN OVERLAY IS NEVER A COLUMN.

   This sat inside the <=900px media query, because that is the only width
   where it is ever seen. Above it, it stayed an ordinary child of `.ax-app`
   -- a two-column grid -- so it took the second column and the main content
   fell to row two at 228px wide, underneath the nav. The whole console, on
   every desktop.

   Being out of flow is not a question a breakpoint gets to answer. What the
   breakpoint decides is whether it is ever VISIBLE, which is the opacity and
   the pointer-events, and those still live in the media query. */
.ax-app__scrim {
  position: fixed;
  inset: 0;
  z-index: 59;
  background: rgba(9, 24, 42, .34);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-panel) var(--ease);
}

.ax-app {
  display: grid;
  grid-template-columns: 228px minmax(0, 1fr);
  height: 100%;
  min-height: 720px;
  background: var(--bg-canvas);
}

.ax-nav {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: var(--space-25) var(--space-25) var(--space-2);
  gap: var(--space-05);
  background: var(--bg-sunken);
  border-right: 1px solid var(--border-subtle);
}

/* the workspace chip — ambient, contained, always has an exit */
.ax-ws {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-width: 0;
  height: 40px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-md);
  text-align: left;
}
.ax-ws:hover { background: var(--component-bg-hover); }
/* Without a menu it is a label: same treatment as a flat `Pick`, which keeps
   its shape so the nav reads the same either way. */
.ax-ws--flat { cursor: default; }
.ax-ws--flat:hover { background: transparent; }
.ax-nav__me--flat { cursor: default; }
.ax-nav__me--flat:hover { background: transparent; }
.ax-ws__mark {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-md);
  background: var(--brand-solid);
  color: var(--text-on-solid);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0;
}
.ax-ws__txt { min-width: 0; flex: 1; }
.ax-ws__name {
  font-size: 12.5px;
  font-weight: 550;
  line-height: 15px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ax-ws__sub {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font-size: 11px;
  line-height: 14px;
  color: var(--accent-text);
  white-space: nowrap;
}
.ax-ws__stop { color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px; }

/* The product mark. It sits above the workspace chip and it is the only
   logo in the system: the chip under it is the company, which is a different
   question. Left-aligned to the same rail as everything else in the nav. */
.ax-nav__brand {
  display: flex;
  align-items: center;
  height: 34px;
  padding: 0 var(--space-25);
  margin-bottom: var(--space-05);
}

.ax-nav__lbl {
  padding: var(--space-25) var(--space-2) var(--space-1);
  font-size: 11px;
  font-weight: 500;
  line-height: 14px;
  color: var(--text-tertiary);
}

.ax-nav__i {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 29px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-md);
  font-size: 12.5px;
  color: var(--text-secondary);
  transition: background var(--dur-fade) var(--ease), color var(--dur-fade) var(--ease);
}
.ax-nav__i svg { color: var(--text-tertiary); transition: color var(--dur-fade) var(--ease); }
.ax-nav__i:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-nav__i.is-active { background: var(--component-bg-selected); color: var(--text-primary); font-weight: 550; }
.ax-nav__i.is-active svg { color: var(--brand-text); }
.ax-nav__i span { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* the count sits on the door it opens — this is why the dashboard needs no tiles */
.ax-nav__n {
  flex: 0 0 auto;
  min-width: 18px;
  padding: 0 5px;
  border-radius: var(--radius-pill);
  font-size: 10.5px;
  font-weight: 550;
  line-height: 16px;
  text-align: center;
  color: var(--text-tertiary);
  background: var(--component-bg);
}
.ax-nav__i.is-active .ax-nav__n { background: var(--bg-canvas); color: var(--text-secondary); }
.ax-nav__n--due { background: var(--warning-subtle); color: var(--warning-text); }

/* Twenty nav items on a short window used to squash every row below its own
   height. Nothing in the nav shrinks; the nav scrolls instead. */
.ax-nav > * { flex: 0 0 auto; }
.ax-nav__spacer { flex: 1 1 auto; min-height: 12px; }

.ax-nav__me {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 34px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-md);
  font-size: 12.5px;
}
.ax-nav__me:hover { background: var(--component-bg-hover); }
.ax-av {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-pill);
  background: var(--component-bg-selected);
  color: var(--text-secondary);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0;
}

/* ------------------------------------------------------------------ main --- */
.ax-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--bg-canvas);
}

/* BAND 1 — title, live meta, one primary action. Nothing else, ever. */
.ax-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 0 0 auto;
  min-height: 52px;
  padding: var(--space-25) var(--space-6);
}
.ax-head__txt { min-width: 0; flex: 1; }
.ax-head__t {
  font-size: 17px;
  font-weight: 600;
  line-height: 23px;
  letter-spacing: -0.015em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ax-head__act { display: flex; align-items: center; gap: var(--space-15); flex: 0 0 auto; }

/* THE OVERFLOW EXISTS AT THE WIDTH WHERE THE SECONDARIES ARE HIDDEN, and
   nowhere else. Above it every action is already on screen, and a menu of them
   is a door into the room you are standing in -- which is what it was, on
   thirty-five screens, for as long as it had been drawn. */
/* Two classes, not one. `.ax-btn` says `display: inline-flex`, it is also a
   single class, and it is declared further down -- so a tie goes to it and the
   dots stayed on screen at every width. Measured: visible at 1440 with nothing
   behind it, which is the bug this rule exists to remove. */
.ax-head__act .ax-head__more { display: none; }

/* inline dot-separated meta — replaces the metadata strip that used to be a band */
.ax-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 var(--space-15);
  margin-top: 1px;
  font-size: 12px;
  line-height: 16px;
  color: var(--text-tertiary);
  min-width: 0;
}
.ax-meta > * { white-space: nowrap; }
/* AFTER, NOT BEFORE. Drawn on the item that follows it, the separator
   became the first thing on every wrapped line -- "· 19 priced by nobody"
   opening a line with a mark that has nothing to its left to separate. On
   the item that precedes it, a wrap leaves the dot at the END of the line
   above, which is the mark that says the sentence continues. */
.ax-meta > *:not(:last-child)::after { content: "\00b7"; margin-left: var(--space-15); color: var(--border-strong); }
.ax-meta b { font-weight: 450; color: var(--text-secondary); }

/* BAND 2 — the controls. View, search, preferences. Then data. */
.ax-bar {
  display: flex;
  align-items: center;
  gap: var(--space-15);
  flex: 0 0 auto;
  min-height: 38px;
  padding: 0 var(--space-6) var(--space-2);
  overflow-x: auto;
  scrollbar-width: none;
}
.ax-bar::-webkit-scrollbar { display: none; }
/* The bar scrolls sideways rather than squeezing its controls into two lines:
   a filter that wraps mid-word stops reading as one thing. */
.ax-bar > * { flex: 0 0 auto; }
.ax-bar__gap { flex: 1 1 auto; min-width: 0; }
.ax-pick, .ax-tok, .ax-tok > * { white-space: nowrap; }

.ax-body { flex: 1; min-height: 0; padding: 0 24px 40px; }
.ax-body--flush { padding: 0; }

/* ============================================================================
   2 · CONTROLS — a border is earned. Only what you can type into, or what
   floats, gets one.
   ========================================================================== */

/* `min-height` is not belt and braces. `.ax-grow` is `flex: 1`, which is right
   in a row and destructive in a column: with a zero basis a button collapses
   to its line box, and the sign-in button of the first application built on
   this system rendered <b>19px tall</b> next to 32px fields.

   A control defends its own size. Whatever the parent's layout asks for, a
   button is never shorter than a button. */
.ax-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-15);
  height: var(--ctl-md);
  min-height: var(--ctl-md);
  padding: 0 var(--ctl-pad-md);
  border-radius: var(--radius-md);
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--dur-fade) var(--ease), border-color var(--dur-fade) var(--ease), color var(--dur-fade) var(--ease);
}
.ax-btn svg { width: 14px; height: 14px; }

.ax-btn--primary { background: var(--brand-solid); color: var(--text-on-solid); }
.ax-btn--primary:hover { background: var(--brand-solid-hover); }

.ax-btn--secondary { border: 1px solid var(--border-interactive); color: var(--text-primary); background: var(--bg-canvas); }
.ax-btn--secondary:hover { background: var(--component-bg); }

.ax-btn--ghost { color: var(--text-secondary); }
.ax-btn--ghost:hover { background: var(--component-bg-hover); color: var(--text-primary); }


.ax-btn--icon { width: var(--ctl-md); padding: 0; color: var(--text-tertiary); }
.ax-btn--icon.ax-btn--sm { width: var(--ctl-sm); }
.ax-btn--icon.ax-btn--lg { width: var(--ctl-lg); }
.ax-btn--icon:hover { background: var(--component-bg-hover); color: var(--text-primary); }

/* LA ESCALA DE CONTROL, con nombre. Ninguno de estos tres numeros es nuevo: md
   es lo que `.ax-btn` ya dibujaba y lg es lo que `--lg` ya dibujaba. Lo que
   faltaba no era una escala, eran los nombres — el censo de la hoja encontro
   SIETE alturas de control (24, 26, 28, 30, 32, 34) y ninguna se llamaba nada.
   sm sale de los botones del calendario, que ya median 24. */
.ax-btn--sm { height: var(--ctl-sm); min-height: var(--ctl-sm); padding: 0 var(--ctl-pad-sm); font-size: 12px; }
.ax-btn--sm svg { width: 13px; height: 13px; }
.ax-btn--lg { height: var(--ctl-lg); min-height: var(--ctl-lg); padding: 0 var(--ctl-pad-lg); font-size: 13px; }

/* The one place a button spans its container: the submit of a form that is
   the whole screen, and a drawer footer on a phone. It is a variant rather
   than a utility because `width: 100%` on an inline-flex button is the kind
   of thing every consumer writes slightly differently. */
.ax-btn--block { display: flex; width: 100%; }
/* ===== APAGADO: DOS MECANISMOS, Y NO SON LO MISMO =====================
   Aqui habia `opacity: .45` y en el pager `opacity: .35`. Medido el contraste
   resultante contra su propio fondo, en los dos temas y con los dos colores de
   texto que un boton puede heredar: 7 de 8 combinaciones por debajo de 3:1, la
   peor en 1.68:1. Un control apagado que nadie puede leer no dice "apagado",
   dice "esta pantalla esta rota".
   Y la causa no es el numero elegido: una OPACIDAD no puede prometer contraste,
   porque el resultado depende del fondo que le toque debajo. Tres colores si
   pueden, y estan derivados contra el PEOR de los dos fondos en que el texto
   vive: el lienzo y la superficie del boton relleno.

   [disabled]              el control NO APLICA aqui. Fuera del orden de foco,
                           por especificacion del navegador.
   [aria-disabled="true"]  el control aplica pero ahora no se puede usar — falta
                           un dato, hay algo en curso. SIGUE enfocable, que es
                           justo lo que hace falta para poder decir por que.

   Y se va `pointer-events: none`: no era decoracion. Sacaba el control del
   arbol enfocable, asi que un usuario de teclado no podia ni descubrirlo — y de
   paso la metrica de foco de esta misma suite no lo podia medir. */
.ax-btn[disabled],
.ax-btn[aria-disabled="true"] {
  color: var(--text-disabled);
  cursor: not-allowed;
}
.ax-btn--primary[disabled],
.ax-btn--primary[aria-disabled="true"] {
  background: var(--component-bg-disabled);
  color: var(--text-disabled);
}
.ax-btn--secondary[disabled],
.ax-btn--secondary[aria-disabled="true"] {
  border-color: var(--border-disabled);
  background: var(--bg-canvas);
}
/* lo que apaga de verdad es que no responda, no que se destiña */
.ax-btn[disabled]:hover,
.ax-btn[aria-disabled="true"]:hover { background: inherit; color: var(--text-disabled); }
.ax-btn--primary[disabled]:hover,
.ax-btn--primary[aria-disabled="true"]:hover { background: var(--component-bg-disabled); }
.ax-btn--secondary[disabled]:hover,
.ax-btn--secondary[aria-disabled="true"]:hover { background: var(--bg-canvas); }

/* a "select" that is a word and a caret, not a boxed dropdown */
.ax-pick {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: var(--ctl-md);
  padding: 0 7px 0 8px;
  border-radius: var(--radius-md);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-primary);
}
.ax-pick:hover { background: var(--component-bg-hover); }
.ax-pick svg { width: 13px; height: 13px; color: var(--text-tertiary); }
/* Without a menu it is a label, so it stops offering the hand a control
   would. It keeps its shape: the bar reads as one row either way. */
/* A SEGMENTED CONTROL. One border around the set, one line between each pair:
   the group is the thing, and the buttons inside it are parts of it rather
   than three separate controls that happen to be touching. */
.ax-seg {
  /* A GRID, so the parts are equal. As a flex row every segment was as wide
     as its own word and the dividers landed wherever the words happened to
     end; `1fr` columns in an auto-width container all resolve to the widest,
     which is what "one object divided into equal parts" means. */
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-md);
  overflow: hidden;
  flex: 0 0 auto;
}
.ax-seg__i {
  display: inline-flex;
  align-items: center;
  /* The label centred in a column the grid above made equal to its siblings. */
  justify-content: center;
  gap: var(--space-15);
  border: 0;
  border-right: 1px solid var(--border-interactive);
  background: var(--bg-surface);
  font: inherit;
  font-size: 12px;
  /* CONSTANT, and not a third way of saying which one is on. At 500 selected
     and 400 otherwise, the pressed segment was the widest one in the set, so
     every divider shifted on every click. */
  font-weight: 500;
  color: var(--text-secondary);
  padding: 0 var(--space-3);
  height: var(--row-default);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-tint) var(--ease), color var(--dur-tint) var(--ease);
}
.ax-seg__i:last-child { border-right: 0; }
.ax-seg__i:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-seg__i[aria-pressed="true"] {
  background: var(--component-bg-selected);
  color: var(--text-primary);
  cursor: default;
}
.ax-seg__i:focus-visible { outline: 2px solid var(--border-focus); outline-offset: -2px; }

.ax-pick--flat { cursor: default; }
.ax-pick--flat:hover { background: transparent; }

.ax-pick__n { color: var(--text-tertiary); font-weight: 400; }

.ax-search {
  display: flex;
  align-items: center;
  gap: var(--space-15);
  height: 28px;
  width: 210px;
  padding: 0 9px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-canvas);
  color: var(--text-tertiary);
}
.ax-search:hover { border-color: var(--border-interactive); }
.ax-search:focus-within { border-color: var(--border-focus); box-shadow: 0 0 0 3px var(--brand-subtle); }
.ax-search input { flex: 1; min-width: 0; border: 0; outline: 0; background: none; font-size: 12.5px; }
.ax-search input::placeholder { color: var(--text-tertiary); }
/* The full-width one is also taller: it is the control the panel is about,
   not one of five things in a bar. */
.ax-search--block { width: 100%; height: 34px; padding: 0 var(--space-25); }

/* filter tokens: text with a dismiss, not a boxed pill */
/* -- CHIPS -- a filter row over one list. Quiet at rest, filled when chosen,
   and carrying the count because the count is the reason to look at it. */
.ax-chips { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.ax-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-15);
  height: var(--ctl-md);
  padding: 0 var(--space-25);
  border: 0;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--text-secondary);
  font: inherit;
  font-size: 12.5px;
  font-weight: 450;
  white-space: nowrap;
  cursor: pointer;
}
.ax-chip:hover { background: var(--component-bg); color: var(--text-primary); }
.ax-chip.is-on { background: var(--component-bg-selected); color: var(--text-primary); font-weight: 550; }
/* Tabular figures, so a row of counts does not shuffle sideways when one of
   them goes from 9 to 10. */
.ax-chip__n { color: var(--text-tertiary); font-variant-numeric: tabular-nums; }
.ax-chip.is-on .ax-chip__n { color: var(--text-secondary); }
.ax-chip:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 1px; }

.ax-tok {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: var(--ctl-sm);
  padding: 0 var(--space-15) 0 var(--space-2);
  border-radius: var(--radius-md);
  background: var(--component-bg);
  font-size: 12px;
  color: var(--text-primary);
}
.ax-tok__k { color: var(--text-tertiary); }
.ax-tok__x { display: grid; place-items: center; flex: 0 0 auto; width: 15px; height: 15px; border-radius: var(--radius-sm); color: var(--text-tertiary); }
.ax-tok__x:hover { background: var(--component-bg-selected); color: var(--text-primary); }

/* ----------------------------------------------------------------- state ---
   A state at rest is a dot and a word. The filled chip is for the exception:
   something is late, failed, or needs you. Ten chips in ten rows is noise. */
.ax-state { display: inline-flex; align-items: center; gap: var(--space-15); font-size: 12.5px; color: var(--text-primary); white-space: nowrap; }
.ax-dot { flex: 0 0 auto; width: 6px; height: 6px; border-radius: var(--radius-pill); background: var(--text-tertiary); }
.ax-dot--success { background: var(--success-solid); }
.ax-dot--warning { background: var(--warning-solid); }
.ax-dot--danger  { background: var(--danger-solid); }
.ax-dot--info    { background: var(--info-solid); }
.ax-dot--accent  { background: var(--accent-solid); }
.ax-dot--idle    { background: var(--border-strong); }
.ax-dot--ring    { background: none; box-shadow: inset 0 0 0 1.5px var(--border-strong); }

.ax-flag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 20px;
  padding: 0 7px;
  border-radius: var(--radius-md);
  font-size: 11.5px;
  font-weight: 500;
  white-space: nowrap;
}
.ax-flag--warning { background: var(--warning-subtle); color: var(--warning-text); }
.ax-flag--danger  { background: var(--danger-subtle);  color: var(--danger-text); }
.ax-flag--success { background: var(--success-subtle); color: var(--success-text); }
.ax-flag--ai      { background: var(--accent-subtle);  color: var(--accent-text); }
.ax-flag--info    { background: var(--info-subtle);    color: var(--info-text); }

/* Inline code is a quotation from the product: a field name, a route, a value
   the API returns. Seven cards use it and nothing styled it -- it rendered in
   the browser's default monospace, at the browser's default size, inside a
   12.5px paragraph. It reads as a quotation now, not as emphasis. */
code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  padding: 1px var(--space-1);
  border-radius: var(--radius-sm);
  background: var(--component-bg);
  color: var(--text-secondary);
}

.ax-flag--quiet   { background: var(--component-bg);   color: var(--text-secondary); }

/* the machine's mark. Orange means Aucto did this, and only that. */
.ax-ai { display: inline-flex; align-items: center; gap: 5px; color: var(--accent-text); font-size: 12px; }
.ax-ai svg { width: 12px; height: 12px; }

/* ============================================================================
   3 · TABLE — no box, no shouting header, one line per row, always.
   ========================================================================== */

/* `relative`, y es el arreglo de un defecto que el minimo de la tabla saco a
   la luz: un scroller que no contiene lo que lleva dentro no es un scroller.
   El `.ax-vh` de la cabecera de acciones es `position:absolute`, y sin un
   ancestro posicionado su bloque contenedor estaba por ENCIMA del envoltorio,
   asi que no lo recortaba nadie. Mientras la tabla cupo su posicion estatica
   caia dentro; en cuanto la tabla pidio mas de lo que hay, ese span de 1px
   aterrizo fuera de la ventana y estiro el documento entero --- 126px en
   Approvals, 376 en AdminAIPrompts. Un texto invisible desplazando la pagina.
   Los popovers de la tabla son `fixed` y `relative` no les hace de bloque
   contenedor, asi que siguen saliendo por encima de todo. Comprobado: 98. */
/* Y la barra es la del sistema, no la que ponga el navegador: desde hoy esta
   tabla desplaza de verdad en 33 cards a 1024, asi que esa barra se ve --- y
   una barra gruesa y gris dentro de una tabla fina parece justo lo que Wagner
   fotografio, un fallo. */
.ax-tblwrap { position: relative; overflow: auto; scrollbar-width: thin; scrollbar-color: var(--border-interactive) transparent; }

/* `fixed`, and it is not a detail: with auto layout a declared column width is
   a suggestion, and one long title quietly widens its column until the table
   is wider than the page. "A row is one line" is not enforceable while the
   content gets a vote -- the cell ellipsises only if something stopped it
   growing first. Fixed makes the declared widths true and hands the leftover
   to the columns that did not ask for one. */
/* ...Y CUANDO NO SOBRA NADA, LA QUE NO PIDIO SE QUEDA SIN NADA. Eso es lo que
   la frase de arriba no dice. Medido a 1024: `Title` en 2px en Approvals, 8 en
   DispositionTicket, 28 en Orders. La columna que se sacrifica es siempre la
   misma -- la que no declara anchura es la que lleva el nombre -- de modo que
   la tabla se comia justo el dato por el que existe la fila.
   `--tbl-min` lo calcula la fabrica sumando lo que las columnas pidieron de
   verdad. Por debajo de eso el envoltorio desplaza, que es para lo que lleva
   `overflow:auto` desde el primer dia. */
.ax-tbl { width: 100%; table-layout: fixed; min-width: var(--tbl-min, 0); }
/* Plegada en dos lineas la tabla ya no tiene columnas, asi que alli el minimo
   no aplica --- eso se apaga abajo, en el bloque del pliegue.
   En un peek si aplica, solo que es OTRO: esa vista deja vivas unicamente las
   columnas con `fold`, y la fabrica suma tambien ese subconjunto. Poner cero
   aqui fue mi primer intento y el gate lo canto en la primera ejecucion ---
   `Title` en 10px en AssetPeek a 1100. */
.ax-app--peek .ax-tbl { min-width: var(--tbl-min-peek, 0); }

.ax-tbl th {
  height: 28px;
  padding: 0 10px 7px;
  vertical-align: bottom;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 15px;
  text-align: left;
  color: var(--text-tertiary);
  white-space: nowrap;
  /* UNA CABECERA NO PINTA FUERA DE SU COLUMNA. `.ax-tbl td` recorta desde
     siempre; el `th` solo llevaba `nowrap`, asi que al estrecharse la columna
     la celda callaba y el encabezado se derramaba sobre el vecino. Medido a
     1024: `Title` sobre `Waiting on` en Approvals, `What they did` en
     AdminActivities, `Asset name` en BulkImport.
     Lo de arriba ya lo decia -- "a header sits over its own column" -- y era
     lo unico que no lo cumplia. */
  overflow: hidden;
  text-overflow: ellipsis;
  border-bottom: 1px solid var(--border-subtle);
  background: none;
  text-transform: none;
  letter-spacing: 0;
}
.ax-tbl th:first-child, .ax-tbl td:first-child { padding-left: 0; }
/* The left edge is flush, because text starts there and the eye reads rightward.
   The right edge is not: a number aligned right against a hard edge reads
   cramped, so the column keeps one gutter step of air. */
.ax-tbl th:last-child,  .ax-tbl td:last-child  { padding-right: var(--space-4); }

/* A header sits over its own column. `.ax-tbl th` is more specific than
   `.ax-tbl__num`, so the numeric header was quietly left-aligned. */
.ax-tbl th.ax-tbl__num, .ax-tbl th.ax-tbl__act { text-align: right; }
/* Las dos que no se recortan: el hit del select-all mide 26px con margenes
   negativos de -6, y la gutter de acciones saca sus botones de la caja a
   proposito. Recortarlas seria aplicar la regla donde no es el problema. */
.ax-tbl th.ax-tbl__sel, .ax-tbl th.ax-tbl__act { overflow: visible; text-overflow: clip; }

/* The select-all label carries a 36px hit area for the thumb. In the header
   that height had nothing to do with the header, and inflated the band from
   23px to 44px on every table with checkboxes. */
.ax-tbl thead .ax-hit { height: 28px; }
.ax-tbl th.ax-tbl__sel { padding-bottom: 0; vertical-align: middle; }

/* LAS TRES DENSIDADES, de verdad. El README anunciaba tres desde la fase 0 y
   `tokens.json` publicaba una; al publicar las tres, el gate de tokens canto
   las dos nuevas como vocabulario que nadie habla — y tenia razon. Esto es lo
   que faltaba: las dos variantes que las hacen existir.
   La fila es otra escala que el control: un boton de 28 dentro de una fila de
   36 es correcto, y confundir las dos es lo que tuvo esta familia con un solo
   token durante meses. */
.ax-tbl--compact td { height: var(--row-compact); }
.ax-tbl--roomy td   { height: var(--row-roomy); }
.ax-tbl td {
  height: var(--row-default);
  padding: 0 var(--space-25);
  border-bottom: 1px solid var(--border-subtle);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 300px;
  vertical-align: middle;
}
.ax-tbl tbody tr { transition: background var(--dur-tint) var(--ease); }
/* THE LAST ROW DOES NOT CLOSE THE TABLE. Inside a section, whatever comes
   next draws its own rule -- `.ax-sec` has a border-top -- so the last row's
   rule and that one land a padding apart and read as a double line. The row
   keeps its rule everywhere else: a table at the foot of a screen, above a
   pager, still needs an edge, because nothing under it is drawing one. */
.ax-sec .ax-tbl tbody tr:last-child td { border-bottom: 0; }

/* THE ROW THAT JUST CHANGED STAYS LIT. Not an animation in the sense the rest
   of this section means -- nothing moves. It is a memory: you pressed
   something, the table re-drew, and without this you have to find your row
   again by reading. It holds long enough to look up and come back. */
.ax-tbl tbody tr.is-lit { animation: ax-lit var(--dur-hold) var(--ease) both; }
/* The same mark on a property. A dialog that writes into a record and closes
   leaves you on whatever part of it you were reading, and on a record this
   long that is usually not the part it wrote to. */
.ax-prop.is-lit {
  animation: ax-lit var(--dur-hold) var(--ease) both;
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 4px var(--brand-subtle);
}
@keyframes ax-lit {
  0%, 45% { background: var(--brand-subtle); }
  100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .ax-tbl tbody tr.is-lit { animation: none; background: var(--brand-subtle); }
  .ax-prop.is-lit { animation: none; background: var(--brand-subtle); }
}
.ax-tbl tbody tr:hover { background: var(--bg-sunken); }
.ax-tbl__id { color: var(--text-tertiary); font-size: 12.5px; }
.ax-tbl__num { text-align: right; }
.ax-tbl__t { font-weight: 450; }
.ax-tbl__sub { color: var(--text-tertiary); font-size: 12px; }
.ax-tbl__sel { width: 26px; }
.ax-tbl__sel input { accent-color: var(--brand-solid); width: 13px; height: 13px; margin: 0; display: block; }
.ax-hit { display: grid; place-items: center; width: 26px; height: var(--row-default); margin: 0 -6px; cursor: pointer; }

/* row actions stay in the DOM and on the tab order; they stop drawing a column */
/* `width: 1%` used to mean "as narrow as the content allows", because with
   auto layout the browser grew it to fit anyway. Under `table-layout: fixed`
   1% is one percent, and the button inside it overflowed the whole table --
   `View as company` landed 1414px into a 1440px window, outside the table it
   belongs to, and no metric in the audit noticed. A real width, then, and one
   the consumer can override when the action has a longer name. */
/* NOTHING IN HERE IS TEXT, SO NOTHING IN HERE ELLIPSISES. `.ax-tbl td`
   clips its overflow and draws a `...` for what it cut, which is right for a
   category that does not fit and wrong for a cell whose only child is a
   28px button in 22px of content box: the browser painted the ellipsis on
   every row, the button underneath it was `opacity: 0`, and the result was
   three dots on every row of every table that nobody had drawn.

   Reported three times, and looked for in the button three times. */
/* `td.ax-tbl__act`, not `.ax-tbl__act`: `.ax-tbl td` is one specificity
   point heavier and was winning, which is why the first attempt at this
   changed nothing and the dots were still there. */
.ax-tbl td.ax-tbl__act { overflow: visible; text-overflow: clip; }
.ax-tbl__act { width: 48px; text-align: right; }
.ax-tbl--actsw .ax-tbl__act { width: var(--acts-w, 156px); }
.ax-tbl__act > * { opacity: 0; transition: opacity var(--dur-tint) var(--ease); }
.ax-tbl tr:hover .ax-tbl__act > *,
.ax-tbl__act:focus-within > * { opacity: 1; }

/* Except when the action IS the list.

   Hiding row actions until hover is right for the incidental ones -- the
   menu of things you could also do -- and wrong for the one the screen
   exists to offer. The staff Companies list is there so somebody can get
   into a company; hiding `View as company` until the pointer arrives turns
   a one-click job into "click the row, read a record, find the button".

   `Table({ actions: 'always' })`. The test is not importance, it is this:
   if the action were removed, would the list still have a reason to exist? */
.ax-tbl--acts .ax-tbl__act > * { opacity: 1; }

/* a status that is a distribution of quantities cannot be one word */
/* `max-width` is what makes the ellipsis below real. Without it the flex box
   sizes to its content, the text never runs out of room, and the cell clips it
   mid-word with a hard edge -- an ellipsis written in the stylesheet and never
   once drawn, which is the second one of those this system has had. */
.ax-split { display: inline-flex; align-items: center; gap: 7px; min-width: 0; max-width: 100%; }
.ax-split__bar { display: flex; width: 54px; height: 5px; border-radius: var(--radius-pill); overflow: hidden; background: var(--component-bg); flex: 0 0 auto; }
.ax-split__bar i { display: block; height: 100%; }
.ax-split__txt { flex: 0 1 auto; min-width: 0; font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ax-split__txt b { font-weight: 450; }
.ax-split__txt span { color: var(--text-tertiary); }

.ax-sel {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 36px;
  font-size: 12.5px;
}
.ax-sel__n { font-weight: 550; }
.ax-sel__gap { flex: 1; }

.ax-foot {
  display: flex;
  align-items: center;
  gap: var(--space-25);
  padding: var(--space-3) 0 0;
  font-size: 12px;
  color: var(--text-tertiary);
}
.ax-foot__gap { flex: 1; }

/* ============================================================================
   4 · RECORD — rail + one scroll + property rows.
   This is what replaces tabs and what replaces the form grid.
   ========================================================================== */

.ax-rec {
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
  min-height: 0;
}
.ax-rec--media { grid-template-columns: 168px minmax(0, 1fr) 232px; }

/* the rail shows every section AND its count at once. That is the whole point:
   you never click to find out whether there is a document. */
.ax-rail { position: sticky; top: 0; display: flex; flex-direction: column; gap: 1px; padding-top: var(--space-05); }
.ax-rail__i {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 27px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-md);
  font-size: 12.5px;
  color: var(--text-secondary);
}
.ax-rail__i:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-rail__i.is-active { background: var(--component-bg-selected); color: var(--text-primary); font-weight: 550; }
.ax-rail__i span { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ax-rail__n { font-size: 11.5px; color: var(--text-tertiary); }

.ax-sec { padding: 22px 0; border-top: 1px solid var(--border-subtle); }
.ax-sec:first-child { padding-top: var(--space-05); border-top: 0; }
/* `--sec-stick-bg` is the surface this header paints when it sticks, and it
   is named rather than hardcoded because a section header does not know what
   it is sitting on: on the page that is the canvas, in a drawer it is the
   raised surface. It only sticks on a phone -- see the media query -- but the
   surface belongs with the component, not with the breakpoint. */
.ax-sec__h { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); }
.ax-sec__t { font-size: 12.5px; font-weight: 550; color: var(--text-secondary); }
.ax-sec__gap { flex: 1; }

/* ---- property rows: text at rest, a box only when you are in it ---------- */
.ax-props { display: flex; flex-direction: column; }
.ax-prop { display: flex; align-items: flex-start; gap: var(--space-3); min-height: 30px; }
.ax-prop__k .ax-dot { margin-right: 5px; }
.ax-prop__k {
  flex: 0 0 auto;
  width: 148px;
  padding-top: var(--space-15);
  font-size: 12px;
  line-height: 18px;
  color: var(--text-tertiary);
}
.ax-prop__v { flex: 1; min-width: 0; }

.ax-val {
  display: block;
  width: 100%;
  min-height: 30px;
  padding: 5px 8px;
  margin-left: -8px;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: none;
  font-size: 13px;
  line-height: 18px;
  text-align: left;
  color: var(--text-primary);
  transition: background var(--dur-tint) var(--ease), border-color var(--dur-tint) var(--ease);
}
.ax-val:hover { background: var(--component-bg); }
/* This used to set `outline: 0` and lean on a 1px border plus a 3px
   brand-subtle glow. The focus metric measured that glow at 1.13:1 against
   the surface -- emphasis, not an indicator -- and the border underneath it
   was 1px, under the 2px a focus ring owes the keyboard. So the ring comes
   back and the pale glow goes: it was decorating a control that had nothing
   to decorate yet. */
.ax-val:focus, .ax-val:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 0;
  background: var(--bg-canvas);
  border-color: var(--border-focus);
}
.ax-val.is-empty { color: var(--text-tertiary); }
.ax-val--wrap { white-space: normal; }
textarea.ax-val { resize: vertical; min-height: 56px; }

/* ============================================================================
   5b · THE CONTROLS A PROPERTY CAN HOLD

   Until now a property row could only be typed into, so every value that the
   product actually picks — site, asset class, who rigs it, how it is packed —
   was drawn as free text. It looked right in a screenshot and was a lie.

   All of them keep the same three states as .ax-val: transparent at rest, a
   surface on hover, a border and a ring on focus. A control never announces
   itself before you reach for it.
   ========================================================================== */

/* — SELECT — a value with a caret that appears only when the row is live.
   At rest it is indistinguishable from text, which is the point: 41 property
   rows must not read as 41 boxes. */
.ax-val--pick { display: flex; align-items: center; gap: var(--space-15); cursor: pointer; }
.ax-val--pick > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ax-val--pick > svg {
  flex: 0 0 auto;
  color: var(--text-tertiary);
  opacity: 0;
  transition: opacity var(--dur-tint) var(--ease);
}
.ax-val--pick:hover > svg,
.ax-val--pick:focus > svg,
.ax-val--pick.is-open > svg { opacity: 1; }

/* the list it opens. The check marks what is chosen; nothing is highlighted
   just for being first. */
.ax-pop__search {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 30px;
  margin: -2px 0 4px;
  padding: 0 var(--space-2);
  color: var(--text-tertiary);
  font-size: 12.5px;
}
.ax-pop__search input { all: unset; flex: 1; min-width: 0; color: var(--text-primary); }
.ax-pop__i .ax-pop__ck { flex: 0 0 auto; margin-left: auto; color: var(--brand-text); opacity: 0; }
.ax-pop__i.is-on .ax-pop__ck { opacity: 1; }
.ax-pop__i.is-on { font-weight: 550; }
.ax-pop__n { margin-left: auto; color: var(--text-tertiary); font-size: 11.5px; }

/* — UNIT — the unit is part of the value, not an ornament beside it. USD, in,
   lb, mm, kg: the product stores the number and the unit separately and shows
   them together, so the row does too. */
.ax-val--unit { display: flex; align-items: baseline; gap: var(--space-1); }
.ax-val--unit:focus-within {
  background: var(--bg-canvas);
  border-color: var(--border-focus);
  box-shadow: 0 0 0 3px var(--brand-subtle);
}
.ax-bare { all: unset; flex: 0 1 auto; min-width: 0; font: inherit; color: var(--text-primary); }
.ax-bare::placeholder { color: var(--text-tertiary); }
.ax-unit { flex: 0 0 auto; color: var(--text-tertiary); }
.ax-unit--pre { order: -1; }

/* — COUNTER — 0/125 on fourteen empty fields is not information. The count
   belongs to the moment you are typing, so it appears with the box and sits
   inside it: no row grows, nothing moves. */
.ax-prop__v { position: relative; }
.ax-count {
  position: absolute;
  right: 8px;
  bottom: 6px;
  font-size: 11px;
  line-height: 14px;
  color: var(--text-tertiary);
  background: var(--bg-canvas);
  padding-left: var(--space-15);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-tint) var(--ease);
}
.ax-prop:focus-within .ax-count { opacity: 1; }
.ax-count.is-close { color: var(--warning-text); }

/* — SWITCH — a thing you do, not a state you read, so it is the one control
   that draws itself at rest. It needs no ON/OFF beside it: the switch is the
   word. Blue, because blue is you — orange stays Aucto's. */
.ax-sw {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 30px;
  margin-left: -8px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-md);
  background: none;
  font-size: 13px;
  color: var(--text-primary);
  cursor: pointer;
}
.ax-sw:hover { background: var(--component-bg); }
/* The glow stays as emphasis; the ring stays because it is the indicator.
   The old comment said an outline clips at the ends of a pill -- it does
   not, browsers have followed border-radius on outlines for years, and
   the claim was a reason invented after the fact. */
.ax-sw:focus-visible { box-shadow: 0 0 0 3px var(--brand-subtle); }
.ax-sw__t {
  position: relative;
  flex: 0 0 auto;
  width: 28px;
  height: 16px;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  transition: background var(--dur-rise) var(--ease);
}
.ax-sw__t::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #fff;
  transition: transform var(--dur-rise) var(--ease);
}
.ax-sw.is-on .ax-sw__t { background: var(--brand-solid); }
.ax-sw.is-on .ax-sw__t::after { transform: translateX(12px); }
.ax-sw__sub { color: var(--text-tertiary); font-size: 12px; }

/* — CHECK and RADIO — outside a table, where the label is the target. */
.ax-cb {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  min-height: var(--ctl-md);
  padding: var(--space-1) var(--space-2) var(--space-1) 0;
  border-radius: var(--radius-md);
  font-size: 13px;
  line-height: 20px;
  cursor: pointer;
}
.ax-cb input { accent-color: var(--brand-solid); width: 14px; height: 14px; margin: 3px 0 0; flex: 0 0 auto; }
.ax-cb__b { min-width: 0; }
.ax-cb__sub { display: block; color: var(--text-tertiary); font-size: 12px; line-height: 17px; }

/* — CALENDAR — inside the same popover shell as every other menu, so a date
   is not a different species of control. */
.ax-cal { width: 224px; }
.ax-cal__h { display: flex; align-items: center; gap: var(--space-1); height: 28px; margin-bottom: var(--space-1); font-size: 12.5px; font-weight: 550; }
.ax-cal__h b { flex: 1; text-align: center; font-weight: 550; }
.ax-cal__nav { width: 24px; height: var(--ctl-sm); border-radius: var(--radius-md); color: var(--text-tertiary); display: grid; place-items: center; }
.ax-cal__nav:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-cal__g { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; }
.ax-cal__w { height: 22px; display: grid; place-items: center; font-size: 10.5px; color: var(--text-tertiary); }
.ax-cal__d { height: 28px; border-radius: var(--radius-md); font-size: 12px; color: var(--text-primary); }
.ax-cal__d:hover { background: var(--component-bg-hover); }
.ax-cal__d.is-out { color: var(--text-tertiary); opacity: .55; }
.ax-cal__d.is-today { font-weight: 650; color: var(--brand-text); }
.ax-cal__d.is-sel { background: var(--brand-solid); color: var(--text-on-solid); }
.ax-cal__f { display: flex; align-items: center; gap: var(--space-15); margin-top: var(--space-1); }
.ax-cal__f button { height: var(--ctl-sm); padding: 0 7px; border-radius: var(--radius-md); font-size: 12px; color: var(--text-secondary); }
.ax-cal__f button:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-cal__gap { flex: 1; }

/* — DESTRUCTIVE — red is not a colour a control wears, it is a thing that
   happens when you reach for it. At rest a delete reads like any other action;
   under the pointer it says what it is, while nothing has happened yet. */
.ax-btn--danger { color: var(--text-secondary); }
.ax-btn--danger:hover,
.ax-btn--danger:focus-visible { background: var(--danger-subtle); color: var(--danger-text); }

/* In the confirmation the red IS the subject, not a warning about something
   you might do, so there it is red at rest — the one place in the system. */
.ax-btn--doom { background: var(--danger-solid); color: var(--text-on-solid); }
.ax-btn--doom:hover { background: var(--danger-text); }

.ax-pop__i--danger:hover { background: var(--danger-subtle); color: var(--danger-text); }
.ax-btn--icon.is-danger:hover { background: var(--danger-subtle); color: var(--danger-text); }

/* — WAITING — a skeleton is a promise about shape, so it has the shape of
   what is coming and never of a spinner. */
.ax-skel {
  display: block;
  height: 13px;
  border-radius: var(--radius-sm);
  background: var(--component-bg);
  animation: ax-pulse var(--dur-pulse) var(--ease-loop) infinite;
}
.ax-skel + .ax-skel { margin-top: var(--space-2); }
@keyframes ax-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .ax-skel { animation: none; } }

/* — TOOLTIP — for the sentence a label cannot hold. Never for the label. */
.ax-tip { position: relative; display: inline-flex; align-items: center; }
.ax-tip__q { color: var(--text-tertiary); display: inline-flex; }
.ax-tip__q:hover { color: var(--text-secondary); }
.ax-tip__b {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 7px);
  transform: translateX(-50%);
  z-index: 40;
  width: max-content;
  max-width: 260px;
  padding: 7px 9px;
  border-radius: var(--radius-md);
  background: var(--text-primary);
  color: var(--bg-raised);
  font-size: 12px;
  line-height: 17px;
  box-shadow: var(--shadow-popover);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-fade) var(--ease);
}
.ax-tip:hover .ax-tip__b, .ax-tip:focus-within .ax-tip__b, .ax-tip.is-open .ax-tip__b { opacity: 1; }

/* — AVATAR — initials, because a generated face is a lie about a person. */
.ax-av {
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-pill);
  background: var(--component-bg-selected);
  color: var(--text-secondary);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .01em;
}
.ax-av--lg { width: 32px; height: 32px; font-size: 12px; }
.ax-av--me { background: var(--brand-subtle); color: var(--brand-text); }

/* — PAGER — the count is the sentence; the arrows are the smallest part. */
.ax-pager { display: flex; align-items: center; gap: var(--space-25); font-size: 12.5px; color: var(--text-tertiary); }
.ax-pager b { color: var(--text-primary); font-weight: 550; }
.ax-pager__gap { flex: 1 1 auto; min-width: var(--space-2); }

/* The count is one line. In a flex row a span shrinks below its content and
   wraps, and `Select` arrives carrying `.ax-val { width: 100% }` from the
   property row it was built for -- together they turned "1-8 of 12 assets"
   into three lines the first time this shipped inside an application. */
.ax-pager > span { white-space: nowrap; }
.ax-pager .ax-val { width: auto; flex: 0 0 auto; }
.ax-pager__n { display: flex; align-items: center; gap: var(--space-05); }
.ax-pager__n button { width: 26px; height: var(--ctl-sm); border-radius: var(--radius-md); display: grid; place-items: center; color: var(--text-tertiary); }
.ax-pager__n button:hover { background: var(--component-bg-hover); color: var(--text-primary); }
/* el mismo apagado que el boton, con el mismo token: eran dos opacidades
   distintas (.45 y .35) para la misma idea, y ninguna de las dos se leia. */
.ax-pager__n button[disabled] { color: var(--text-disabled); cursor: not-allowed; }

@media (max-width: 640px) {
  .ax-pager__n button { width: 32px; height: var(--ctl-lg); }
  .ax-tip__b { max-width: 220px; }
}

/* — THUMBNAIL — 24px, because a 36px row is what Polaris calls a tightly
   condensed layout and 24 is the size it gives for one. A rounded square, not
   a circle: circles are for people, and a circular crop cuts the corners off a
   rectangular photograph — which on this product is where the nameplate is. */
.ax-th { position: relative; display: inline-flex; flex: 0 0 auto; }
/* 28, WHICH IS WHAT A ROW IS ALREADY FULL OF: the header's hit areas are
   28px, the row action is 28px. At 24 a photograph of a machine was a smudge
   you could not read, and the point of putting it in the row is that you can
   tell the boring mill from the dicer without opening either. */
.ax-th__i {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--component-bg);
  color: var(--text-tertiary);
  background-image: linear-gradient(135deg, var(--component-bg) 0%, var(--component-bg-selected) 100%);
}
.ax-th__i svg { width: 15px; height: 15px; }
.ax-th--none .ax-th__i { background-image: none; background: none; box-shadow: inset 0 0 0 1px var(--border-subtle); }

/* A real photograph, when there is one. `cover` and not `contain`, because at
   24px a letterboxed machine is four grey bars and a smudge; the crop loses
   the edges and keeps the thing. The big one below does the opposite. */
.ax-th__i img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* the larger look, in the same popover shell as every other menu — a preview
   is not a different species of surface either */
.ax-th__pop {
  position: absolute;
  left: 0;
  top: calc(100% + 6px);
  z-index: 60;
  /* Big enough to decide from. This is somebody looking at a machine to say
     whether it goes on the marketplace, and 212px of card around a 148px
     image is a thumbnail with a frame, not a look. */
  width: 272px;
  padding: var(--space-15);
  display: none;
}
/* display:none, not opacity:0 — a hidden preview that still has a box is a
   second line inside the row, and the audit was right to count it. */
.ax-th:hover .ax-th__pop, .ax-th:focus-within .ax-th__pop, .ax-th.is-open .ax-th__pop {
  display: block;
  animation: ax-fade var(--dur-fade) var(--ease) both;
}

/* A PREVIEW HAS TO LEAVE THE ROW TO BE A PREVIEW.

   It could not, and it never could: three ancestors clip it and every one of
   them is doing its job. The cell clips so a long title ellipsises. The table
   wrapper clips so a wide table scrolls sideways. The body clips so a long
   page scrolls. An absolutely positioned popover inside a clipped box is a
   popover nobody can see — the component has drawn this one since the day the
   thumbnail shipped and it was 210px of correctly rendered nothing.

   Two ways out and only one of them is honest. Turning off the cell's
   overflow gives the title back its full width in the middle of a table whose
   whole promise is one line per row; the wrapper cannot help at all, because
   CSS forces the other axis to `auto` the moment one axis is not `visible`.

   So it leaves the flow entirely. `position: fixed` escapes every clip, and
   anchor positioning puts it back beside the thumb it belongs to — no
   JavaScript, no measuring, and `flip-block` moves it above the row by itself
   when the row is near the bottom of the window. `anchor-scope` is what makes
   one name safe on 128 rows: without it every preview would anchor to the
   last thumbnail on the page. */
@supports (anchor-name: --a) and (anchor-scope: --a) {
  .ax-th { anchor-name: --ax-th; anchor-scope: --ax-th; }
  /* Two class names, because `.ax-pop` sets `position: absolute` 400 lines
     further down and at equal weight the last one wins. */
  .ax-th .ax-th__pop {
    position: fixed;
    position-anchor: --ax-th;
    top: anchor(bottom);
    left: anchor(left);
    margin: var(--space-15) 0 0;
    position-try-fallbacks: flip-block, flip-inline;
  }
}
@keyframes ax-fade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .ax-th__pop { animation: none !important; } }
.ax-th__big {
  display: grid;
  place-items: center;
  height: 186px;
  border-radius: var(--radius-md);
  background-image: linear-gradient(135deg, var(--component-bg) 0%, var(--component-bg-selected) 100%);
  color: var(--text-tertiary);
}
.ax-th__big svg { width: 26px; height: 26px; }
/* `contain` here: this is the look, and a look that crops is a look that lies
   about what you are about to list. */
.ax-th__big { overflow: hidden; }
.ax-th__big img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* IT WRAPS, AND IT HAS TO BE TOLD TO.

   The thumb lives in the title cell, and `.ax-item__t` sets `white-space:
   nowrap` so a long title ellipsises on one line. `position: fixed` takes the
   preview out of that cell's CLIP, not out of what it inherits from it, so
   the sentence was drawn on a single line half the window wide and the card
   ended where the image did. */
.ax-th__cap { display: block; white-space: normal; padding: var(--space-15) var(--space-1) var(--space-05); font-size: 11.5px; line-height: 16px; color: var(--text-tertiary); }
.ax-th__cap b { color: var(--text-primary); font-weight: 550; }

/* title cell: the thumb, then the words, still one line */
.ax-item { display: flex; align-items: center; gap: 9px; min-width: 0; }
.ax-item__t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 450; }

@media (max-width: 640px) {
  /* The folded row is two lines and 71px tall. A 28px thumbnail in it was
     sized for the 36px row it is not in any more. */
  .ax-th__i { width: 36px; height: 36px; }
  .ax-th__i svg { width: 17px; height: 17px; }
  .ax-th__pop { width: 180px; }
}

.ax-props__more {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  margin: 6px 0 0 -8px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-md);
  font-size: 12px;
  color: var(--text-tertiary);
}
.ax-props__more:hover { background: var(--component-bg-hover); color: var(--text-primary); }

.ax-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 40px; }

/* ---- readiness: what this record is good for, per channel.
   Not a new field — it is the required fields you already have, counted. ---- */
.ax-ready { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; font-size: 12.5px; }
.ax-ready__k { color: var(--text-tertiary); font-size: 12px; }
.ax-ready__i { display: inline-flex; align-items: center; gap: var(--space-15); white-space: nowrap; }
.ax-ready__i b { font-weight: 450; }
.ax-ready__i u { text-decoration: none; color: var(--text-tertiary); }
.ax-ready__i.is-short u { color: var(--warning-text); text-decoration: underline; text-underline-offset: 2px; }

/* ---- media: one hero and a filmstrip. Never five grey squares. ---------- */
.ax-media { position: sticky; top: 2px; display: flex; flex-direction: column; gap: var(--space-2); }
.ax-media__hero {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  /* A button, so it has to be told not to look like one. The photograph is
     the surface; there is nothing left for a border to say. */
  border: 0;
  padding: 0;
  width: 100%;
  border-radius: var(--radius-lg);
  background: var(--component-bg);
  color: var(--text-tertiary);
  font: inherit;
  font-size: 12px;
  overflow: hidden;
}
.ax-media__hero.has-img { cursor: zoom-in; }
.ax-media__hero:focus-visible, .ax-media__th:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}
.ax-media__hero img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ax-media__hero.has-img { background: var(--bg-sunken); position: relative; }
/* Quiet until you are on the photograph, like every other action that lives
   inside another object. */
.ax-media__zoom {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--bg-raised);
  box-shadow: var(--shadow-popover);
  color: var(--text-secondary);
  opacity: 0;
  cursor: pointer;
  transition: opacity var(--dur-tint) var(--ease);
}
.ax-media__hero:hover .ax-media__zoom,
.ax-media__zoom:focus-visible { opacity: 1; }

/* The strip beside the hero rather than under it. Same as the record below
   1180px -- see the note on `row`. */
.ax-media--row {
  position: static;
  display: grid;
  /* The hero takes the room, not the strip: the point of the row is to stop
     a photograph sitting next to 260px of grey, and the answer to that is a
     BIGGER photograph, not a smaller one with company. */
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  gap: var(--space-25);
  /* IT STOPS -- unless the strip is the whole set. A 1.55fr hero across a
     1150px section is a 690px photograph over a 660px column of four
     thumbnails, most of a screen spent on a preview. `--wide` below is the
     other answer: keep the width, spend it on the strip. */
  max-width: 640px;
}

/* THE HERO TAKES THE HEIGHT THE STRIP TAKES. It had `aspect-ratio: 4/3`,
   which decides a height from a width -- so beside a strip that grows a row
   every two photographs, the photograph stayed 4:3 and the column beside it
   ran past it. Wagner: "podemos aumentar la altura de la img principal para
   que haga match con la cajita de agregar foto".

   In the row layout the STRIP is what knows how tall this section is, so the
   hero stops having an opinion and fills what is there. `min-height` keeps it
   from going squat on a record with one photograph, where the strip is a
   single add box. */
/* THE HERO IS EXACTLY AS TALL AS THE STRIP. It had `aspect-ratio: 4/3`,
   which decides a height from a width -- so beside a column that grows a row
   every two photographs, the photograph stayed 4:3 while the strip ran past
   it. Wagner: "podemos aumentar la altura de la img principal para que haga
   match con la cajita de agregar foto".

   It spans the STRIP's row and no more. Spanning both rows handed it the note
   underneath as well, so it overshot the add box by the height of a line of
   text -- which is the same wrong answer in the other direction. The strip is
   the thing that knows how tall this section is; the hero stops having an
   opinion and matches it. */
.ax-media--row .ax-media__hero {
  grid-row: 1;
  align-self: stretch;
  aspect-ratio: auto;
  /* No floor. The strip decides, which is the whole point -- a floor is the
     hero having an opinion again, and it showed up immediately as a 56px
     overhang on a record with three photographs. */
  min-height: 0;
}
/* AND THE PHOTOGRAPH LEAVES THE FLOW. Dropping the aspect ratio was not
   enough: the `<img>` still reported its own height at that width, so the
   grid row sized to the PICTURE and the strip beside it ended wherever it
   ended -- 404px of hero next to 195px of strip on a portrait shot, which is
   the original complaint with a different cause behind it.

   Out of the flow, the hero has no content height at all, so the only thing
   left that can size the row is the strip. Which is the answer. */
.ax-media--row .ax-media__hero.has-img { position: relative; }
.ax-media--row .ax-media__hero img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ax-media--row .ax-media__strip { grid-template-columns: repeat(2, minmax(0, 1fr)); align-content: start; }
.ax-media--row .ax-media__n { align-self: end; }
/* THE RECORD'S OWN ARRANGEMENT. The hero takes a share and stops; the strip
   fills the rest with as many columns as fit, so thirteen photographs are two
   rows beside the photograph instead of a table underneath it.

   The hero has a floor and a ceiling rather than an aspect ratio: it follows
   the strip, which is the thing that knows how many photographs there are,
   and stops following it before it turns into a portrait. */
.ax-media--wide { max-width: none; grid-template-columns: minmax(0, 360px) minmax(0, 1fr); }
.ax-media--wide .ax-media__strip {
  grid-template-columns: repeat(auto-fill, minmax(94px, 1fr));
  align-content: start;
}
.ax-media--wide .ax-media__hero { min-height: 240px; max-height: 400px; }
.ax-media__hero img { width: 100%; height: 100%; object-fit: contain; display: block; }
.ax-media__strip { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-15); }
/* MINMAX(0) AND MIN-WIDTH 0, BOTH. A grid item with an aspect ratio takes its
   automatic minimum size from its own height, and `1fr` is `minmax(auto, 1fr)`
   -- so the add box measured itself, pushed its column wider, grew taller
   because the column was wider, and settled 30px bigger than the column next
   to it. Two columns asked to be equal came out 97 and 127.

   It only appeared when the tiles stopped being uniform: a tile with a caption
   already carries `min-width: 0`, and the bare squares beside it did not.

   `align-self: start` is the other half. A square in a row with a captioned
   tile was stretched to the caption's height, so the add box stood taller than
   the photograph above it. The squares keep their own size and line up with
   the photographs. */
.ax-media__strip > * { min-width: 0; }
.ax-media__add, .ax-media__slot { align-self: start; }
.ax-media__th {
  /* THE OVERLAY'S CONTEXT. The count is `inset: 0` inside the thumbnail, and
     the thumbnail was not positioned -- so while it sat inside a captioned
     tile it borrowed that tile's context, and the moment the counter tile
     stopped needing a caption the `+6` resolved against an ancestor the size
     of the panel and landed across the hero. */
  position: relative;
  aspect-ratio: 1;
  border: 0;
  padding: 0;
  border-radius: var(--radius-md);
  background: var(--component-bg);
  overflow: hidden;
  cursor: zoom-in;
}
.ax-media__th img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ax-media__th.is-active { box-shadow: 0 0 0 2px var(--brand-solid); }

/* A TILE THAT CARRIES ITS JOB. The photograph, what it is for, and the mark
   that takes it off -- three buttons side by side, because a button inside a
   button is not a thing. */
.ax-media__t { position: relative; display: flex; flex-direction: column; gap: var(--space-05); min-width: 0; }
.ax-media__t .ax-media__th { width: 100%; }
/* Quiet until the pointer is on the photograph, like the hero's zoom mark. */
.ax-media__x {
  position: absolute;
  top: var(--space-05);
  right: var(--space-05);
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--bg-raised);
  box-shadow: var(--shadow-popover);
  color: var(--text-secondary);
  opacity: 0;
  cursor: pointer;
  transition: opacity var(--dur-tint) var(--ease);
}
.ax-media__t:hover .ax-media__x, .ax-media__x:focus-visible { opacity: 1; }
.ax-media__x:hover { color: var(--danger-text); }
/* THE CAPTION IS THE PICKER. It reads as a label at rest and grows its caret
   under the pointer: one line per tile that is either a job or a warning, and
   never a control sitting there asking to be filled in. */
.ax-media__cap {
  display: flex;
  align-items: center;
  gap: var(--space-05);
  min-height: 28px;
  padding: 0 var(--space-1);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  font: inherit;
  font-size: 11.5px;
  text-align: left;
  cursor: pointer;
}
.ax-media__cap:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-media__cap.is-empty, .ax-media__cap.is-warn { color: var(--warning-text); }
.ax-media__cap svg { flex: 0 0 auto; opacity: 0; transition: opacity var(--dur-tint) var(--ease); }
.ax-media__t:hover .ax-media__cap svg, .ax-media__cap:focus-visible svg { opacity: 1; }
.ax-media__cap:focus-visible, .ax-media__x:focus-visible, .ax-media__slot:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}
/* WHAT IS NOT HERE, NAMED. An empty required slot used to look exactly like
   an empty tile, which is the finding that started this. */
.ax-media__slot {
  display: grid;
  place-items: center;
  align-content: center;
  gap: var(--space-05);
  aspect-ratio: 1;
  padding: var(--space-05);
  border: 1px dashed var(--warning-solid);
  border-radius: var(--radius-md);
  background: var(--warning-subtle);
  color: var(--warning-text);
  font: inherit;
  cursor: pointer;
}
.ax-media__slot:hover { border-style: solid; }
.ax-media__slotl { font-size: 10.5px; line-height: 1.2; text-align: center; }
/* The rest of the set, counted on the last one it drew. Same value as the
   scrim, because it is the same idea: what is underneath, stepped back. */
.ax-media__more {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(9, 24, 42, .54);
  color: #fff;
  font-size: 13px;
  font-weight: 500;
}
[data-theme="dark"] .ax-media__more { background: rgba(0, 0, 0, .6); }
.ax-media__add {
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  border: 1px dashed var(--border-interactive);
  border-radius: var(--radius-md);
  color: var(--text-tertiary);
}
.ax-media__add:hover { border-color: var(--brand-solid); color: var(--brand-text); }
.ax-media__n { font-size: 11.5px; color: var(--text-tertiary); }

/* ---- the condition scale: eleven steps, grey at one end and green at the
   other. The ramp is the scale's, not the value's. --------------------------- */
.ax-scale { display: flex; flex-direction: column; gap: var(--space-1); }
/* A row of its own for the marker, so the bar never moves when the value
   changes -- a label that pushes the thing it labels is a label that makes the
   page twitch on every keystroke. */
.ax-scale__m { position: relative; height: 22px; }
/* No transform here: the component sets it, and only for the steps where
   centring does not push the chip out of the panel. */
.ax-scale__t {
  position: absolute;
  bottom: 0;
  padding: 2px var(--space-15);
  border-radius: var(--radius-sm);
  background: var(--text-primary);
  color: var(--bg-surface);
  font-size: 11.5px;
  font-weight: 500;
  white-space: nowrap;
}
.ax-scale__b { display: flex; gap: var(--space-05); }
.ax-scale__s {
  flex: 1;
  height: 10px;
  min-width: 0;
  border: 0;
  padding: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.ax-scale__s:first-child { border-top-left-radius: var(--radius-sm); border-bottom-left-radius: var(--radius-sm); }
.ax-scale__s:last-child { border-top-right-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm); }
/* The step you are on is taller, because a ring around a 10px bar is thicker
   than the bar. */
.ax-scale__s.is-on { height: 16px; margin-top: -3px; }
.ax-scale__s:hover { height: 14px; margin-top: -2px; }
.ax-scale__s:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.ax-scale__n { font-size: 12.5px; color: var(--text-secondary); }

/* ---- the merge: two columns and a choice per row ------------------------- */
.ax-merge { display: flex; flex-direction: column; }
.ax-merge__r {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--space-2);
  min-height: var(--row-default);
  border-bottom: 1px solid var(--border-subtle);
}
.ax-merge__r--h { min-height: 38px; }
.ax-merge__r:last-child { border-bottom: 0; }
.ax-merge__f { font-size: 13px; color: var(--text-primary); }
.ax-merge__r--h .ax-merge__f { font-size: 12px; color: var(--text-tertiary); }
.ax-merge__c {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-15) var(--space-2);
  border-radius: var(--radius-md);
}
/* The chosen side is tinted, so the answer reads down a column rather than
   one radio at a time. */
.ax-merge__c.is-on { background: var(--brand-subtle); }
.ax-merge__c--h { padding-top: 0; padding-bottom: 0; }
.ax-merge__c input { margin-top: 0; }
.ax-merge__v { min-width: 0; font-size: 13px; overflow-wrap: anywhere; }
.ax-merge__v.is-empty { color: var(--text-tertiary); }

/* ---- the composer: one toolbar, two places ------------------------------- */
.ax-comp { display: flex; flex-direction: column; gap: var(--space-15); }
/* QUIET UNTIL YOU REACH FOR IT, like every other value in this system. The
   toolbar used to draw seven buttons over an empty box: chrome with nothing
   to act on, and three visual blocks for a section nobody had written in yet.
   It comes with the cursor, which is the only moment `bold` means anything.

   It FADES rather than opens, and it keeps its place while it is invisible.
   Opening it would move the box down by its own height at the exact moment
   the cursor lands in the box -- the one moment nothing should move.

   `pointer-events` goes with the fade: an invisible button that can still be
   clicked is worse than a visible one. The keyboard is unaffected, because
   tabbing to a button puts focus inside `.ax-comp`, which is what shows it. */
.ax-comp__t {
  display: flex;
  align-items: center;
  gap: var(--space-05);
  flex-wrap: wrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-tint) var(--ease);
}
.ax-comp:focus-within .ax-comp__t { opacity: 1; pointer-events: auto; }
.ax-comp__b {
  display: grid;
  place-items: center;
  min-width: 28px;
  height: 28px;
  padding: 0 var(--space-1);
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  color: var(--text-secondary);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.ax-comp__b:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-comp__b:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.ax-comp__b s { text-decoration: line-through; }
.ax-comp__box {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-25);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-lg);
  background: var(--bg-canvas);
}
.ax-comp__box:focus-within { border-color: var(--border-focus); box-shadow: 0 0 0 3px var(--brand-subtle); }
.ax-comp__in {
  flex: 1;
  min-width: 0;
  min-height: 42px;
  outline: 0;
  font-size: 13px;
  line-height: 19px;
}
/* The placeholder is an attribute rather than a value, because a
   contenteditable with text in it is a box somebody has already typed into. */
.ax-comp__in:empty::before { content: attr(data-ph); color: var(--text-tertiary); }
.ax-comp__in ul, .ax-comp__in ol { margin: var(--space-1) 0; padding-left: var(--space-5); }
.ax-comp__in a { color: var(--accent-text); }
.ax-comp__clip, .ax-comp__go {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  color: var(--text-tertiary);
  cursor: pointer;
}
.ax-comp__clip:hover, .ax-comp__go:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-comp__clip:focus-visible, .ax-comp__go:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.ax-comp__n { display: flex; justify-content: flex-end; }

/* an activity line, not an activity table */
.ax-log { display: flex; flex-direction: column; gap: 0; }
.ax-log__i { display: flex; gap: var(--space-25); align-items: baseline; min-height: 26px; font-size: 12.5px; }
.ax-log__w { flex: 0 0 auto; width: 92px; color: var(--text-tertiary); font-size: 12px; }
.ax-log__t { flex: 1; min-width: 0; }
.ax-log__t b { font-weight: 450; }

/* ============================================================================
   5 · COMPOSER — the drawer that is not a wizard.
   You type the two things that identify the object; everything else appears
   below in one scroll. The footer says what the record is ready for, not what
   step you are on.
   ========================================================================== */

/* THE HOST, WHICH THE SYSTEM NOW MAKES ITSELF. It used to be two ids a
   consumer had to remember to put in its own HTML, and a consumer that forgot
   got a console where nothing opened and no error anywhere. */
.ax-layer { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
.ax-layer:empty { display: none; }
.ax-layer > * { pointer-events: auto; }

.ax-scrim { position: fixed; inset: 0; background: rgba(9, 24, 42, .34); animation: ax-in var(--dur-fade) var(--ease); }
[data-theme="dark"] .ax-scrim { background: rgba(0, 0, 0, .55); }
.ax-scrim.is-leaving { animation: ax-out var(--dur-panel) var(--ease) both; }

@keyframes ax-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes ax-out { from { opacity: 1; } to { opacity: 0; } }

/* THE DRAWER SLIDES. It used to appear whole, which reads as a page swap
   rather than as something arriving beside what you were reading, and the
   first person to use it said so: "sale de golpe".

   AND IT LEAVES THE SAME WAY -- same duration, same curve, played backwards,
   because "equally smooth" is not a slower or faster number, it is the same
   number. Transform only: the system never animates layout, which is also why
   none of this can jank. */
.ax-layer .ax-dw { animation: ax-slide var(--dur-panel) var(--ease); }
@keyframes ax-slide { from { transform: translateX(100%); } to { transform: none; } }
.ax-layer .ax-dw.is-leaving { animation: ax-slide-out var(--dur-panel) var(--ease) both; }
@keyframes ax-slide-out { from { transform: none; } to { transform: translateX(100%); } }

/* The modal does NOT come from the edge -- coming from the edge is what made a
   delete feel like a second panel stacked on the record. It arrives where it
   will be, slightly under, and settles. The translate has to be written into
   the keyframe because the centring itself is a transform. */
.ax-layer .ax-modal { animation: ax-pop var(--dur-rise) var(--ease); }
@keyframes ax-pop {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 8px)) scale(.985); }
  to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.ax-layer .ax-modal.is-leaving { animation: ax-pop-out var(--dur-rise) var(--ease) both; }
@keyframes ax-pop-out {
  from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  to { opacity: 0; transform: translate(-50%, calc(-50% + 8px)) scale(.985); }
}

/* THIS BLOCK IS ALSO HOW THE JS KNOWS. `close()` holds no duration of its own
   -- it reads `animationDuration` off the node after adding the class, so the
   stylesheet is the only place the number lives. Which means that here, where
   the animation becomes `none`, the duration reads 0s and the panel goes on
   the spot, with no second rule to remember. */
@media (prefers-reduced-motion: reduce) {
  .ax-scrim, .ax-scrim.is-leaving,
  .ax-layer .ax-dw, .ax-layer .ax-dw.is-leaving,
  .ax-layer .ax-modal, .ax-layer .ax-modal.is-leaving { animation: none; }
}

/* Bottom left, out of the way of the primary action, which lives top right. */
.ax-toasts {
  position: fixed;
  left: var(--space-6);
  bottom: var(--space-6);
  z-index: 80;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  pointer-events: none;
}
.ax-toasts > * { pointer-events: auto; animation: ax-rise var(--dur-rise) var(--ease); }
@keyframes ax-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .ax-toasts > * { animation: none; } }

/* A SHORT FORM ASKED ON ITS OWN -- the rows inside `ask`. */
.ax-ask__f { display: flex; flex-direction: column; gap: var(--space-15); }
.ax-ask__f + .ax-ask__f { margin-top: var(--space-4); }
.ax-ask__l { display: flex; align-items: baseline; gap: 3px; font-size: 12px; font-weight: 500; color: var(--text-secondary); }
.ax-ask__f textarea { min-height: 68px; padding: var(--space-2) 9px; resize: vertical; line-height: 18px; }
.ax-req { margin-right: 3px; color: var(--danger-text); font-style: normal; }

/* the two notes a long list grows: how many more, and nothing matched */
.ax-pop__pad { padding: 6px var(--space-2); }

.ax-dw {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  width: 560px;
  max-width: 100%;
  background: var(--bg-raised);
  border-left: 1px solid var(--border-subtle);
  box-shadow: var(--shadow-modal);
}
.ax-dw--wide { width: 760px; }
.ax-dw--narrow { width: 420px; }

/* A peek is the same record seen from the list, and it is NOT a modal.
   No scrim, the list keeps its width and its keyboard, and the row you are on
   stays lit. The day a peek needs a scrim it has stopped being a peek and has
   become the screen — at which point it should be one. */
/* A BORDER IS EARNED. The drawer floats over a scrim, so its shadow is what
   separates it; the peek sits beside the table with nothing dimmed behind it,
   so the shadow has nothing to cast against and a single line does the work. */
.ax-dw--peek { box-shadow: none; border-left: 1px solid var(--border-subtle); }
.ax-app--peek .ax-main { margin-right: 560px; }

/* A list beside a peek has 560px less to work with, and a table that scrolls
   sideways next to the thing explaining one of its rows is the worst of both.

   It drops the columns a PHONE would drop. The priority is already declared
   on every table in this system -- `fold` says which cells survive being
   folded into two lines -- so a second, nearly identical ordering would be
   two lists to keep in agreement. A column worth losing on a phone is a
   column worth losing to a peek, and the peek is showing all of it anyway. */
.ax-app--peek .ax-tbl th:not([data-fold]):not(.ax-tbl__sel):not(.ax-tbl__act),
.ax-app--peek .ax-tbl td:not([data-fold]):not(.ax-tbl__sel):not(.ax-tbl__act) { display: none; }
/* The fill is the whole mark. It used to carry a 2px accent bar on the first
   cell as well: a second way of saying "this is the row you are reading",
   pressed against the table's left edge with no room around it. */
.ax-tbl tbody tr.is-peeked { background: var(--component-bg-selected); }
.ax-kbd {
  display: inline-block;
  min-width: 18px;
  padding: 1px 5px;
  border-radius: var(--radius-sm);
  background: var(--component-bg);
  color: var(--text-secondary);
  font-size: 11px;
  line-height: 16px;
  text-align: center;
}

/* The fourth geometry: the photograph itself, at the size it was taken. No
   header, no footer, nothing to answer -- so every exit is the same exit. */
.ax-viewer {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-12);
}
.ax-viewer__img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: var(--radius-md); display: block; }
.ax-viewer__cap {
  position: absolute;
  left: 50%;
  bottom: var(--space-5);
  transform: translateX(-50%);
  padding: var(--space-15) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--bg-raised);
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
}
.ax-viewer__x { position: absolute; top: var(--space-4); right: var(--space-4); border-radius: var(--radius-md); background: var(--bg-raised); }

/* AT THE EDGES OF THE SCREEN, NOT OF THE PHOTOGRAPH. A portrait shot leaves
   300px of backdrop on either side, and an arrow pinned to the image would
   move every time the next one has a different aspect ratio -- so you would
   be chasing a button that was in a different place for each photo.

   Fixed position, so the target is where it was last time. */
.ax-viewer__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--bg-raised);
  box-shadow: var(--shadow-popover);
  color: var(--text-secondary);
  cursor: pointer;
  transition: color var(--dur-tint) var(--ease), background var(--dur-tint) var(--ease);
}
.ax-viewer__nav:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-viewer__nav:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.ax-viewer__prev { left: var(--space-4); }
.ax-viewer__next { right: var(--space-4); }

/* The caption grew a second thing to say, so it became a row. "3 of 5" is
   not decoration: a carousel that wraps gives you no other way to know you
   have seen them all. */
.ax-viewer__cap { display: flex; align-items: baseline; gap: var(--space-2); }
.ax-viewer__t { min-width: 0; }
.ax-viewer__i { color: var(--text-tertiary); font-variant-numeric: tabular-nums; }

@media (max-width: 640px) {
  .ax-viewer { padding: var(--space-4); }
  .ax-viewer__nav { width: 44px; height: 44px; }
  .ax-viewer__prev { left: var(--space-2); }
  .ax-viewer__next { right: var(--space-2); }
  /* On a phone the arrows sit where the caption was, so it moves up out from
     under them rather than sharing the row. */
  .ax-viewer__cap { bottom: var(--space-12); white-space: normal; text-align: center; flex-wrap: wrap; justify-content: center; }
}

/* A modal is the one surface allowed on top of another, because the thing
   behind it has to stop. It is centred and not docked: the edge is where a
   panel comes from, and a decision is not another panel on the stack.

   It borrows the drawer's three zones because they ARE the drawer's three
   zones — header that does not scroll, body that does, footer that docks.
   Only the geometry is different, which is the whole idea. */
.ax-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  width: 440px;
  max-width: calc(100% - var(--space-8));
  max-height: calc(100% - var(--space-12));
  border-radius: var(--radius-lg);
  background: var(--bg-raised);
  border: 1px solid var(--border-subtle);
  box-shadow: var(--shadow-modal);
  overflow: hidden;
}
.ax-modal--wide { width: 620px; }

.ax-dw__h { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-4) var(--space-5) var(--space-25); flex: 0 0 auto; }
.ax-dw__t { font-size: 15px; font-weight: 600; line-height: 20px; letter-spacing: -0.012em; }
/* `--space-1` at the top, and the sticky header in the media query below
   cancels exactly that: a header stuck at `top: 0` stops at the far side of
   this padding, and the four pixels left over are four pixels of the record
   scrolling between the title and the section header. Measured in the peek
   at 390px: header bottom 69, body top 69, sticky header stuck at 73. */
.ax-dw__b { flex: 1; min-height: 0; padding: var(--space-1) var(--space-5) var(--space-5); }
.ax-dw__f {
  display: flex;
  align-items: center;
  gap: var(--space-25);
  flex: 0 0 auto;
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--border-subtle);
}
.ax-dw__gap { flex: 1; }

/* the identity line — the two fields that let the machine do the rest */
.ax-ident { display: flex; gap: var(--space-25); }
.ax-ident > * { flex: 1; min-width: 0; }
.ax-in {
  width: 100%;
  height: 32px;
  padding: 0 var(--space-25);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-md);
  background: var(--bg-canvas);
  font-size: 13px;
  outline: 0;
}
.ax-in::placeholder { color: var(--text-tertiary); }
.ax-in:focus { border-color: var(--border-focus); box-shadow: 0 0 0 3px var(--brand-subtle); }
.ax-in--title {
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.016em;
}
.ax-in--title:focus { box-shadow: none; }

.ax-hint { font-size: 11.5px; color: var(--text-tertiary); }

/* the machine's answer, inline, with an exit */
.ax-match { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; min-height: 26px; font-size: 13px; }
.ax-match b { font-weight: 500; }

/* a choice that is a sentence, not two large cards */
.ax-choice { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; font-size: 13px; }
.ax-choice__alt { color: var(--brand-text); text-decoration: underline; text-underline-offset: 2px; font-size: 12.5px; }

.ax-drop {
  display: flex;
  align-items: center;
  gap: var(--space-25);
  width: 100%;
  min-height: 52px;
  padding: 0 var(--space-35);
  border: 1px dashed var(--border-interactive);
  border-radius: var(--radius-lg);
  color: var(--text-tertiary);
  font-size: 12.5px;
  text-align: left;
}
.ax-drop:hover { border-color: var(--brand-solid); background: var(--brand-subtle); }
.ax-drop b { font-weight: 500; color: var(--text-primary); }

/* ============================================================================
   6 · TODAY — a worklist, not a scoreboard. Real items, ranked. The counts
   already live on the nav; repeating them in eight boxes was the old design.
   ========================================================================== */

.ax-said { font-size: 19px; font-weight: 500; line-height: 27px; letter-spacing: -0.014em; max-width: 720px; }
/* Inside a thread the same quote is context, not a headline: the header above
   it already carries the title, and two 19px lines in a row is two titles. */
.ax-thread__b .ax-said {
  align-self: stretch;
  font-size: 12px;
  font-weight: 400;
  line-height: 17px;
  letter-spacing: 0;
  color: var(--text-tertiary);
  padding-bottom: var(--space-1);
}
.ax-thread__b .ax-said b { font-weight: 550; color: var(--text-secondary); border: 0; }
.ax-said a, .ax-said b { font-weight: 600; text-decoration: none; border-bottom: 1.5px solid var(--border-strong); }
.ax-said a:hover { border-color: var(--brand-solid); color: var(--brand-text); }
.ax-said em { font-style: normal; color: var(--text-tertiary); }

.ax-wl { display: flex; flex-direction: column; }
.ax-wl__i {
  display: flex;
  align-items: center;
  gap: var(--space-25);
  min-height: 44px;
  padding: 0 var(--space-2);
  margin: 0 -8px;
  border-radius: var(--radius-md);
  border-bottom: 1px solid var(--border-subtle);
  font-size: 13px;
}
.ax-wl__i:hover { background: var(--bg-sunken); }
.ax-wl__k { flex: 0 0 auto; width: 66px; font-size: 11.5px; color: var(--text-tertiary); }
.ax-wl__t { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ax-wl__t b { font-weight: 550; }
.ax-wl__t span { color: var(--text-tertiary); }
.ax-wl__when { flex: 0 0 auto; font-size: 12px; color: var(--text-tertiary); white-space: nowrap; }
.ax-wl__when.is-due { color: var(--warning-text); }
.ax-wl__go { flex: 0 0 auto; width: 92px; text-align: right; }
.ax-wl__go > * { opacity: 0; transition: opacity var(--dur-tint) var(--ease); }
.ax-wl__i:hover .ax-wl__go > *, .ax-wl__go:focus-within > * { opacity: 1; }

/* one bar beats four tiles: it carries the same numbers AND their proportion */
.ax-bars { display: flex; height: 9px; border-radius: var(--radius-pill); overflow: hidden; background: var(--component-bg); }
.ax-bars i { display: block; height: 100%; transition: opacity var(--dur-fade) var(--ease); }
.ax-bars:hover i { opacity: .45; }
.ax-bars i:hover { opacity: 1; }
.ax-key { display: flex; flex-wrap: wrap; gap: 6px 26px; margin-top: var(--space-3); }
.ax-key__i { display: flex; align-items: baseline; gap: 7px; font-size: 12.5px; }
.ax-key__sw { align-self: center; width: 8px; height: 8px; border-radius: var(--radius-sm); flex: 0 0 auto; }
.ax-key__n { font-weight: 600; font-size: 14px; }
.ax-key__l { color: var(--text-tertiary); }

.ax-lede { font-size: 12.5px; color: var(--text-tertiary); }

/* ============================================================================
   7 · IMPORT — the grid is the screen. Mapping lives in the column header,
   errors live in the cell, and there are no steps to walk.
   ========================================================================== */

/* 28px, not the 22px it reads at: this is a tap target on a phone, and it is
   the one control in the import where a miss lands a column in the wrong
   field. The audit caught it only once the component got a card of its own --
   inside the import table it folds away at 390 and was never measured. */
.ax-map {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: 28px;
  padding: 0 5px;
  margin-left: -5px;
  border-radius: var(--radius-md);
  font-size: 11.5px;
  font-weight: 500;
  color: var(--text-secondary);
}
.ax-map:hover { background: var(--component-bg-hover); color: var(--text-primary); }
.ax-map svg { width: 12px; height: 12px; color: var(--text-tertiary); }
.ax-map__src { display: block; font-size: 11px; color: var(--text-tertiary); font-weight: 400; padding: 0 0 var(--space-05); }
.ax-map--unset { color: var(--text-tertiary); }

.ax-fix { display: block; margin-top: var(--space-05); font-size: 11.5px; line-height: 15px; color: var(--text-tertiary); }

/* The one place a row is allowed a second line: an error you are meant to act
   on has to be readable. A correction surface is not a scanning surface. */
.ax-tbl td:has(.ax-fix) { white-space: normal; height: auto; padding-top: var(--space-15); padding-bottom: var(--space-15); }
.ax-tbl tr:has(.ax-fix) td { height: auto; }

.ax-prog { display: flex; align-items: center; gap: var(--space-25); font-size: 12.5px; }
.ax-prog__bar { flex: 0 0 auto; width: 180px; height: 5px; border-radius: var(--radius-pill); background: var(--component-bg); overflow: hidden; display: flex; }
.ax-prog__bar i { display: block; height: 100%; background: var(--success-solid); }

/* ============================================================================
   8 · INBOX — list and thread on one surface. No filter tabs: the list groups
   itself, and the thread carries the asset it is about.
   ========================================================================== */

.ax-inbox { display: grid; grid-template-columns: 318px minmax(0, 1fr); min-height: 0; height: 100%; }
.ax-inbox__l { border-right: 1px solid var(--border-subtle); min-height: 0; display: flex; flex-direction: column; }
.ax-inbox__r { min-height: 0; display: flex; flex-direction: column; }

.ax-grp { padding: var(--space-35) var(--space-4) var(--space-1); font-size: 11px; font-weight: 500; color: var(--text-tertiary); }
.ax-msg {
  display: block;
  width: 100%;
  padding: 9px 16px;
  border-left: 2px solid transparent;
  text-align: left;
}
.ax-msg:hover { background: var(--bg-sunken); }
.ax-msg.is-active { background: var(--component-bg-selected); border-left-color: var(--brand-solid); }
.ax-msg__top { display: flex; align-items: center; gap: 7px; }
.ax-msg__who { flex: 1; min-width: 0; font-size: 12.5px; font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ax-msg__t { font-size: 11.5px; color: var(--text-tertiary); flex: 0 0 auto; }
.ax-msg__s { font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 1px; }
.ax-msg__c { font-size: 11.5px; color: var(--text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ax-msg.is-active .ax-msg__c { color: var(--text-secondary); }
.ax-msg.is-read .ax-msg__who { font-weight: 400; color: var(--text-secondary); }
.ax-msg.is-read .ax-msg__s { color: var(--text-secondary); }

/* `max-width` is the ceiling, `align-self` is what stops a four-word reply
   from being drawn 560px wide inside a stretching column. */
.ax-bub { max-width: 560px; align-self: flex-start; }
.ax-bub__m { display: flex; align-items: baseline; gap: var(--space-2); margin-bottom: 3px; font-size: 12px; color: var(--text-tertiary); }
.ax-bub__m b { font-weight: 550; font-size: 12.5px; color: var(--text-primary); }
.ax-bub__x { padding: 9px 12px; border-radius: var(--radius-lg); background: var(--component-bg); font-size: 13px; line-height: 19px; }
/* The attachment: one line, the clip, the name, the size. It is a link
   when the file is still here to open, and the same shape when it is not. */
.ax-bub__file {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 320px;
  text-decoration: none;
  color: var(--text-primary);
}
.ax-bub__file svg { width: 14px; height: 14px; flex: 0 0 auto; color: var(--text-tertiary); }
.ax-bub__file:hover { background: var(--component-bg-hover); }
.ax-bub__sz { flex: 0 0 auto; font-size: 11.5px; color: var(--text-tertiary); }
.ax-bub--me { align-self: flex-end; }
.ax-bub--me .ax-bub__x { background: var(--brand-subtle); }
.ax-bub--me .ax-bub__m { justify-content: flex-end; }

/* The three zones. The header and the composer are fixed; only the middle
   scrolls. A composer that floats after the last message moves every time the
   thread grows and ends up below the fold on a long one. */
.ax-thread { display: flex; flex-direction: column; min-height: 0; height: 100%; }
.ax-thread--none { justify-content: center; align-items: center; text-align: center; padding: var(--space-6); }
.ax-thread--none .ax-none { padding: 0; }

.ax-thread__h {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 52px;
  padding: var(--space-25) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
}
.ax-thread__t {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.012em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ax-thread__b {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5);
}

.ax-reply { flex: 0 0 auto; padding: var(--space-3) var(--space-5) var(--space-4); border-top: 1px solid var(--border-subtle); }
.ax-reply__box { border: 1px solid var(--border-interactive); border-radius: var(--radius-lg); background: var(--bg-canvas); padding: var(--space-2) var(--space-25); }
.ax-reply__box:focus-within { border-color: var(--border-focus); box-shadow: 0 0 0 3px var(--brand-subtle); }
.ax-reply textarea { width: 100%; border: 0; outline: 0; background: none; resize: none; font-size: 13px; line-height: 19px; min-height: 42px; }

/* ============================================================================
   9 · The rest — empty, popover, toast, danger zone.
   ========================================================================== */

/* No horizontal inset of its own: in a record section the rows beside it
   start at the container's edge too, so an empty state that indented would be
   the one thing out of line. Where the rows ARE inset, the container says so
   -- see the message list below. */
.ax-none { padding: 40px 0; max-width: 420px; }

/* In the message list every row is inset by its own padding plus the 2px it
   keeps for the active marker, and the empty state was the only thing flush
   against the edge. */
.ax-inbox__l .ax-none { padding-left: 18px; padding-right: var(--space-4); }

/* Inside a table it is already in a cell, which has the table's own padding. */
.ax-tbl__none > td { padding-top: 0; padding-bottom: 0; }
.ax-tbl__none .ax-none { padding: var(--space-8) 0; }
.ax-tbl__none:hover { background: transparent; }
.ax-none__t { font-size: 13px; font-weight: 550; margin-bottom: 3px; }
.ax-none__p { font-size: 12.5px; color: var(--text-tertiary); line-height: 18px; }
.ax-none__a { margin-top: var(--space-3); }

.ax-pop {
  position: absolute;
  z-index: 40;
  min-width: 210px;
  padding: 5px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-raised);
  box-shadow: var(--shadow-popover);
}
.ax-pop__i { display: flex; align-items: center; gap: var(--space-2); width: 100%; height: 28px; padding: 0 var(--space-2); border-radius: var(--radius-md); font-size: 12.5px; text-align: left; }
.ax-pop__i:hover { background: var(--component-bg-hover); }
.ax-pop__sep { height: 1px; margin: 5px 0; background: var(--border-subtle); }

/* The wrapper a floating dropdown lives in. It carries the position; the
   `.ax-pop` inside carries the surface, so nothing is drawn twice. The margin
   goes because a dropdown is placed, not spaced.

   NOT `ax-drop`, which I called it first and which the system has meant since
   September: a place to DROP a file. Two meanings of the same four letters,
   and the collision was silent -- the catalog upload's drop zone quietly lost
   its margin and would have kept it for as long as nobody opened that screen
   and the record on the same afternoon. */
.ax-dropdown { margin: 0; }
.ax-dropdown .ax-pop { position: static; max-height: 300px; overflow-y: auto; }

.ax-toast {
  display: flex;
  align-items: center;
  gap: var(--space-25);
  padding: var(--space-25) var(--space-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-raised);
  box-shadow: var(--shadow-popover);
  font-size: 12.5px;
}

.ax-saved { display: inline-flex; align-items: center; gap: var(--space-15); font-size: 12px; color: var(--text-tertiary); }

.ax-danger { padding-top: var(--space-15); }
.ax-danger__p { font-size: 12.5px; color: var(--text-tertiary); line-height: 18px; max-width: 460px; margin-bottom: var(--space-25); }
.ax-danger__p b { color: var(--text-primary); font-weight: 450; }

.ax-note { font-size: 12.5px; line-height: 18px; color: var(--text-secondary); }
.ax-note b { font-weight: 550; color: var(--text-primary); }

.ax-rows { display: flex; flex-direction: column; }
/* A SEPARATOR SEPARATES, so it goes BETWEEN and not under. Every row carried
   a bottom border, which put a line under the last one -- and under the only
   one, on a list of a single item, where there is nothing on either side of
   it to keep apart. Wagner: "no me gusta la segunda linea, dejemos solo la
   linea intermedia, si hay 1 solo no mostramos la linea".

   n rows, n-1 lines. Zero for one. */
.ax-row { display: flex; align-items: center; gap: var(--space-3); min-height: 40px; font-size: 13px; }
.ax-row + .ax-row { border-top: 1px solid var(--border-subtle); }
.ax-row__t { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ax-row__t b { font-weight: 450; }
.ax-row__m { color: var(--text-tertiary); font-size: 12px; white-space: nowrap; }

.ax-money { display: flex; flex-direction: column; }
.ax-money__r { display: flex; align-items: baseline; gap: var(--space-3); min-height: 26px; font-size: 13px; }
.ax-money__k { flex: 1; color: var(--text-secondary); }
.ax-money__v { font-variant-numeric: tabular-nums; }
.ax-money__r--total { border-top: 1px solid var(--border-subtle); margin-top: var(--space-15); padding-top: var(--space-2); font-weight: 600; }

/* The one container that earns a border without floating or holding an input:
   a preview of a surface that is NOT this one. The marketplace seller card is
   a card out there, so drawing it borderless would misrepresent it. It reads
   as a quotation, which is why it also sits on the sunken background. */
.ax-preview {
  max-width: 420px;
  padding: var(--space-35);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-sunken);
}

/* A tree is a list with depth. No connector lines, no folder icons: the indent
   and the disclosure carry the structure. */
.ax-tree { display: flex; flex-direction: column; }
.ax-tree__i {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 32px;
  padding: 0 var(--space-2);
  margin: 0 -8px;
  border-radius: var(--radius-md);
  font-size: 13px;
  text-align: left;
}
.ax-tree__i:hover { background: var(--bg-sunken); }
.ax-tree__tw { flex: 0 0 auto; width: 16px; color: var(--text-tertiary); }
.ax-tree__tw--leaf { visibility: hidden; }
.ax-tree__n { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ax-tree__c { flex: 0 0 auto; font-size: 12px; color: var(--text-tertiary); }
.ax-tree__i[data-d="1"] { padding-left: var(--space-6); }
.ax-tree__i[data-d="2"] { padding-left: var(--space-12); }
.ax-tree__i[data-d="3"] { padding-left: 72px; }

.ax-stack { display: flex; flex-direction: column; }
/* SE OYE Y NO SE VE. No existia, y hacia falta: la cabecera de la columna de
   acciones no lleva titulo visible -una palabra sobre una gutter de iconos es
   una palabra de mas- pero una cabecera sin NINGUN texto es una cabecera vacia
   para quien no ve la tabla. Esto es lo unico que resuelve las dos cosas.
   `clip-path` y no `display:none`: lo segundo tambien lo esconde del lector. */
.ax-vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

.ax-hr { height: 1px; background: var(--border-subtle); }

/* utilities, deliberately few */
.ax-t2 { color: var(--text-secondary); }
.ax-t3 { color: var(--text-tertiary); }
.ax-sm { font-size: 12px; }
.ax-xs { font-size: 11.5px; }
.ax-b  { font-weight: 550; }
.ax-num { font-variant-numeric: tabular-nums; }
.ax-nowrap { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ax-flex { display: flex; align-items: center; }
/* Same rule as the margins, learned the same way: the Thumbnail card asked
   for `ax-gap5` and got no gap at all, because the gap scale stopped at 4
   while the token scale went to 8. */
.ax-gap1 { gap: var(--space-1); } .ax-gap2 { gap: var(--space-2); } .ax-gap3 { gap: var(--space-3); }
.ax-gap4 { gap: var(--space-4); } .ax-gap5 { gap: var(--space-5); } .ax-gap6 { gap: var(--space-6); }
.ax-gap8 { gap: var(--space-8); }
.ax-col { display: flex; flex-direction: column; }
.ax-grow { flex: 1; min-width: 0; }
.ax-mt1 { margin-top: var(--space-1); } .ax-mt2 { margin-top: var(--space-2); } .ax-mt3 { margin-top: var(--space-3); }
/* THE UTILITY SCALE IS THE TOKEN SCALE. It used to be a subset of it --
   there was a --space-5 and no .ax-mt5 -- and five blocks in the
   reporting view asked for the step that was missing and silently got
   nothing, because an unknown class is not an error anywhere. A scale
   with a hole in it is a trap: the hole is invisible from the outside,
   and the name that falls in it is the reasonable one to write. */
.ax-mt4 { margin-top: var(--space-4); } .ax-mt5 { margin-top: var(--space-5); }
.ax-mt6 { margin-top: var(--space-6); } .ax-mt8 { margin-top: var(--space-8); }

/* ============================================================================
   10 · SMALL SCREENS — the same structure, folded.
   The rail disappears (sections stack and keep sticky headings), the table
   becomes two-line rows, the record loses its media column, the inbox shows
   one pane. Nothing turns into tabs on the way down.
   ========================================================================== */

.ax-mobile-only { display: none; }

@media (max-width: 1180px) {
  .ax-rec--media { grid-template-columns: 168px minmax(0, 1fr); }
  .ax-media { position: static; flex-direction: row; align-items: flex-start; gap: var(--space-25); }
  .ax-media__hero { flex: 0 0 200px; }
  .ax-media__strip { flex: 1; grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  .ax-app { grid-template-columns: minmax(0, 1fr); }
  .ax-nav {
    position: fixed;
    z-index: 60;
    top: 0; bottom: 0; left: 0;
    width: 262px;
    transform: translateX(-100%);
    transition: transform var(--dur-panel) var(--ease);
    box-shadow: var(--shadow-modal);
  }
  .ax-app.is-navopen .ax-nav { transform: none; }
  /* Above 900px the nav is a column of the page and there is nothing to
     dismiss, so the scrim never paints -- but it is still OUT OF FLOW, which
     is not a question the breakpoint gets to answer. See the base rule. */
  [data-theme="dark"] .ax-app__scrim { background: rgba(0, 0, 0, .55); }
  .ax-app.is-navopen .ax-app__scrim { opacity: 1; pointer-events: auto; }
  .ax-mobile-only { display: flex; }
  .ax-head { min-height: 48px; padding: var(--space-2) var(--space-4); }
  .ax-bar { padding: 0 var(--space-4) var(--space-2); }
  .ax-body { padding: 0 var(--space-4) var(--space-8); }
  .ax-inbox { grid-template-columns: minmax(0, 1fr); }
  .ax-inbox__l { border-right: 0; }
  .ax-inbox.is-open .ax-inbox__l { display: none; }
  .ax-inbox:not(.is-open) .ax-inbox__r { display: none; }
  .ax-dw, .ax-dw--wide, .ax-dw--narrow { width: 100%; border-left: 0; }
  .ax-app--peek .ax-main { margin-right: 0; }
}

@media (max-width: 760px) {
  .ax-rec, .ax-rec--media { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .ax-rail { display: none; }
  .ax-sec__h { position: sticky; top: 0; z-index: 2; padding: var(--space-2) 0; margin: 0 0 var(--space-15); background: var(--bg-canvas); }
  /* A DRAWER IS NOT THE CANVAS. Stuck flush against the drawer's own header
     -- `.ax-dw__b` pads its top by `--space-1` and a sticky header stops
     below it -- and painting the surface it is actually on. Light mode hid
     the colour, both being white; dark did not, and it was #12171f across a
     #1c2330 record. */
  .ax-dw__b .ax-sec__h { top: calc(var(--space-1) * -1); background: var(--bg-raised); }
  .ax-cols { grid-template-columns: minmax(0, 1fr); }
  .ax-media { flex-direction: column; }
  .ax-media__hero { flex: none; }
  /* The row folds. Beside a 300px column a 1.55fr hero is a stamp, so the
     photograph goes on top at the full width and the strip under it keeps
     filling -- which is the same rule as the wide one, one size down. */
  .ax-media--row, .ax-media--wide { max-width: none; grid-template-columns: minmax(0, 1fr); }
  .ax-media--row .ax-media__hero, .ax-media--wide .ax-media__hero {
    grid-row: auto; aspect-ratio: 4 / 3; min-height: 0; max-height: 300px;
  }

  /* THE HERO WAS THE FIRST TILE, DRAWN AGAIN AND LARGER. The strip starts at
     photo 0 and every tile opens the full viewer, so on a phone the hero cost
     290 of 844 pixels to show a photograph that is already on the screen and
     to offer a way in that the tile beneath it already offers.

     `has-img` only. With no photographs the hero is the empty state and the
     place the first one goes -- that is not a duplicate of anything. And
     with no hero, the ring that marks the tile currently in it marks
     nothing, so it goes too. */
  .ax-media__hero.has-img { display: none; }
  .ax-media__th.is-active { box-shadow: none; }

  /* The strip inherits the room. Five tiles across a 343px screen were 60px
     each; with the hero gone they are the photographs, so they take the size
     a photograph needs. */
  .ax-media--row .ax-media__strip, .ax-media--wide .ax-media__strip {
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  }
  .ax-media__strip { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
  /* A TABLE ON A PHONE IS TWO LINES OF TEXT AND A STATE. It said so and
     then stacked four: the name, an em dash where the Aucto ID would be, the
     status, and the row's three dots on a line of their own.

     The name takes the first line. Everything else flows along the second
     with a gap between -- values, not paragraphs. */
  .ax-tbl--fold { min-width: 0; }
  .ax-tbl--fold thead { display: none; }
  .ax-tbl--fold, .ax-tbl--fold tbody { display: block; width: 100%; }
  .ax-tbl--fold tr {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 var(--space-25);
    width: 100%;
    padding: var(--space-25) 0;
    border-bottom: 1px solid var(--border-subtle);
  }
  /* THE MEDIA OWNS THE GUTTER. Inline with the title, the thumbnail pushed
     the name right and left the second line -- the identifier and the state
     -- to start back at the edge, underneath the photograph. Two left edges
     in a list of a hundred and fifteen rows.

     Out of the flow, into the gutter, and the row's content box starts after
     it: the name, the identifier and the state all begin at the same
     keyline, and none of them needed a rule of their own to get there. This
     is the shape Material, Apple and Polaris all describe -- a leading image
     of about 56, and one column of text beside it. */
  .ax-tbl--fold tr:has(.ax-th) {
    min-height: 76px;
    padding-left: calc(56px + var(--space-3));
  }
  .ax-tbl--fold .ax-th { position: absolute; left: 0; top: var(--space-25); }
  /* Which is also the size at which a photograph of a machine stops being a
     texture and starts being the machine. */
  .ax-tbl--fold .ax-th__i { width: 56px; height: 56px; border-radius: var(--radius-md); }
  .ax-tbl--fold .ax-th__i svg { width: 20px; height: 20px; }
  /* THE NAME IS THE ONLY DOOR, AND HALF A NAME IS HALF A DOOR. One line with
     an ellipsis is survivable on a desktop because the cell carries a
     `title` and the rest is a hover away -- and there is no hover here. Two
     lines fit inside the height the photograph already sets, so the whole
     name costs nothing. */
  .ax-tbl--fold .ax-item__t {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    white-space: normal;
    line-height: 19px;
  }
  .ax-tbl--fold td { display: none; }
  /* EXCEPT THE ONE THAT SAYS THERE IS NOTHING. The fold keeps the cells that
     carry a `data-fold`, and the empty state has none -- it is not a column,
     it spans all of them and it is a sentence. So a table with nothing in it
     drew a 21px stripe of border and padding and said nothing at all, on
     orders, on redeploy and on tickets. The audit already treats this row as
     a sentence rather than a cell; the fold owes it the same. */
  .ax-tbl--fold tr.ax-tbl__none { padding: 0; border-bottom: 0; min-height: 0; }
  .ax-tbl--fold tr.ax-tbl__none td { display: block; height: auto; max-width: none; padding: 0; border: 0; }

  .ax-tbl--fold td[data-fold] {
    display: block;
    height: auto;
    max-width: none;
    padding: 0;
    border: 0;
    font-size: 12px;
    color: var(--text-tertiary);
  }
  /* The name owns the first line, and keeps its right edge clear of the
     action pinned beside it. */
  .ax-tbl--fold td[data-fold="1"] {
    flex: 0 0 100%;
    padding-right: 36px;
    margin-bottom: var(--space-05);
    font-weight: 450;
    font-size: 13px;
    color: var(--text-primary);
  }
  /* Nothing is not a value. The factory marks what it filled with a dash, and
     a folded row drops those rather than spending a line on one. */
  .ax-tbl--fold td.ax-tbl__nil { display: none; }
  /* The action survives the fold. A folded row drops the columns that were
     context and keeps the ones that were the row -- and the button is not
     context, it is the only verb the row had. Dropping it turns "the same
     row, smaller" into "a picture of the row", which is a different promise.
     Found on the asset record's Status table at 442px: six states, a quantity
     in each, and no way to move one. */
  /* Beside the name, not under the row. It was a block, so the only verb a
     row had arrived as a third line of three dots. */
  .ax-tbl--fold td.ax-tbl__act {
    position: absolute;
    top: 6px;
    right: 0;
    display: block;
    width: auto;
    height: auto;
    padding: 0;
    border: 0;
    text-align: right;
  }
  .ax-tbl--fold td.ax-tbl__act:empty { display: none; }
  /* A row with a named action rather than a menu -- `View as company` -- has
     no room beside the title, so that one goes back under the row. */
  /* `margin-left: auto` is the edge: each of these flows after a name of a
     different length, so three `Nudge` buttons ended at three different
     places. A verb that repeats down a list belongs to an edge, and this is
     the same edge the dotted action uses on the rows that have one. */
  .ax-tbl--actsw.ax-tbl--fold td.ax-tbl__act { position: static; margin-top: var(--space-2); margin-left: auto; text-align: right; }

  /* A FILTER BAR ON A PHONE WRAPS. It scrolled sideways with the scrollbar
     hidden, so on a 375px screen the chips ran to 685px and `Released` and
     `Sale` were off the edge with nothing to say so. */
  /* ONE LINE, AND AN EDGE THAT SAYS IT CONTINUES. These wrapped onto two
     and three lines, which is a lot of a phone spent on a row of filters.
     They scroll again -- but a hidden scrollbar ending flush at the edge was
     what made the scroll dishonest the first time, so the row is masked
     where it continues and the chips there are cut mid-colour. A hard edge
     reads as an end; a fade cannot. `Chips` puts these classes on. */
  .ax-chips { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .ax-chips::-webkit-scrollbar { display: none; }
  /* FULL BLEED, SO THE NEXT CHIP CAN ACTUALLY PEEK. Inside the bar's gutter
     the row cut its last chip 16px short of the screen, and a row that stops
     before the edge reads as a row that has ended -- which is the whole
     thing the fade was trying to argue against. The gutter becomes the
     scroller's own padding, so the first chip still lines up with the search
     and the title while the last one runs off the physical edge. */
  /* `--space-4`, which is what the bar pads itself by at this width --
     measured, after `--space-6` overshot the screen by 8px on each side. A
     negative margin that cancels a padding has to name the same token. */
  .ax-bar > .ax-chips {
    margin-left: calc(var(--space-4) * -1);
    margin-right: calc(var(--space-4) * -1);
    padding-left: var(--space-4);
    padding-right: var(--space-4);
    scroll-padding-inline: var(--space-4);
  }
  /* Shorter than it was: the peek is the affordance now, and a long fade
     dissolves the very chip it is meant to reveal. */
  .ax-chips.is-more-r { mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent); }
  .ax-chips.is-more-l { mask-image: linear-gradient(to right, transparent, #000 20px); }
  .ax-chips.is-more-b { mask-image: linear-gradient(to right, transparent, #000 20px, #000 calc(100% - 20px), transparent); }

  .ax-bar { flex-wrap: wrap; overflow-x: visible; row-gap: var(--space-2); }
  .ax-bar > * { flex: 0 1 auto; min-width: 0; }
  .ax-bar__gap { display: none; }
  /* The rest of the line, not the whole of it: beside a 78px scope picker
     that leaves 259px of field, and one row where there were two. */
  .ax-search { flex: 1 1 160px; width: auto; min-width: 160px; }
  /* A CONTROL FOR COLUMNS, WHERE THE TABLE HAS NO COLUMNS. This is the width
     at which the row folds -- two lines and a state, built from the cells
     marked `data-fold` -- so every column the picker offers is one the
     screen is not drawing. The class is how a surface says "this control
     belongs to the width where the table is a table". */
  .ax-bar__wide { display: none; }

  .ax-prop { flex-direction: column; gap: 0; }
  .ax-prop__k { width: auto; padding: var(--space-1) 0 0; }
  .ax-val { margin-left: -8px; }

  .ax-said { font-size: 17px; line-height: 25px; }
  .ax-wl__k { display: none; }
  .ax-wl__go > * { opacity: 1; }
  .ax-tbl__act > * { opacity: 1; }
  .ax-search { width: 100%; }
  .ax-ident { flex-direction: column; }
  .ax-key { gap: 6px 18px; }

  /* A finger is not a cursor. Everything you can press grows to at least 32px
     on the short side; the ones that carry real consequence go to 36. */
  /* en tactil el boton sube al paso grande de la MISMA escala, no a un numero
     suelto: lo que crece es el tamano, no una excepcion. El icono se va por
     encima de la escala a proposito -36- y por eso lleva su propia linea. */
  .ax-btn { height: var(--ctl-lg); padding: 0 var(--space-3); }
  .ax-btn--icon { width: 36px; height: 36px; }
  .ax-pick { height: var(--ctl-lg); }
  .ax-tok { height: 30px; padding-right: var(--space-1); }
  .ax-tok__x { width: 28px; height: var(--ctl-md); }
  .ax-hit { width: 34px; }

  /* On a phone the head keeps one primary and the overflow. Secondary actions
     do not get their own row; they move behind the three dots -- and the dots
     appear at exactly this width, because above it there is nothing behind
     them. `Head` reads which ones are hidden when the button is pressed. */
  .ax-head__act > :not(.ax-btn--primary):not(.ax-btn--icon) { display: none; }
  .ax-head__act .ax-head__more { display: inline-flex; }
  .ax-tbl__sel input { width: 17px; height: 17px; }
  .ax-tbl__sel { width: 34px; }
  .ax-props__more { height: 34px; }
  /* A four-letter inline link ("View") is 26px wide. Padding, not a min-width,
     so it still reads as a word inside the sentence. */
  .ax-choice__alt, .ax-ws__stop, .ax-ready__i u {
    display: inline-flex; align-items: center; min-height: 32px;
    padding: 0 var(--space-15); margin: 0 -6px; border-radius: var(--radius-md);
  }
  /* A SECTION HEADER WRAPS BEFORE IT OVERFLOWS. Three ghost buttons beside
     a title -- Show history, QA status, Ask A+ -- want 390px on a 375px
     screen, and the third one hung off the edge. The head has an overflow
     menu for this; a section header has room on a second line. */
  /* THE NAME OF THE SCREEN IS NOT AN ELLIPSIS. On a phone the head gives the
     title about 138px, and `Virtual Warehouse` wants 148 -- so the one word
     that says where you are was cut. It wraps to a second line instead, which
     the audit allows and a person can read. */
  /* THE NAME OF THE SCREEN IS NOT AN ELLIPSIS. The head gives the title about
     138px on a phone and `Virtual Warehouse` wanted 148, so the one word that
     says where you are was cut by ten pixels.

     Wrapping it was the wrong answer twice over: `line-clamp` hides the third
     line without removing it -- the audit measures line boxes, not paint, and
     went on counting three -- and a head that grows is a head that pushes the
     screen down. Two points of type buys the whole name and keeps the one
     line the rest of the system is built around. */
  /* THE HEAD STACKS RATHER THAN COMPETES. One row for the name and one for
     the verbs: `Ask A+ to price it` is a wide label, and beside a title on a
     375px screen it left `Acc...` -- three characters of the machine you are
     looking at -- and sat on top of the six lines of meta underneath.

     `align-items: flex-start`, because a two-line title beside an icon button
     should not drag the button to its middle. */
  /* TWO BANDS, AND EACH ONE HAS BOTH EDGES OF THE SCREEN.

     It was a pile: a 36px menu button nudged 2px down beside a 21px line, a
     meta wrapping under it, and the actions on a row of their own pushed to
     the right -- three left edges and nothing sharing any of them. */
  .ax-head { flex-wrap: wrap; align-items: flex-start; row-gap: var(--space-2); }
  .ax-head__txt { flex: 1 1 60%; }
  /* The title takes the button's height as its line box, so the two centre
     against each other and the top of the header is one 36px band. The nudge
     that used to do this was a guess at the difference between them. */
  .ax-head__t { font-size: 15px; line-height: 36px; }
  /* AND IT COLLAPSES ONCE YOU ARE READING. 129px of an 812px screen, fixed,
     whatever you were doing. Material's medium and large app bars fold into
     the small one on scroll and stay folded until you are back at the top;
     this is that, on the one part of the header that is context rather than
     control. `App` sets the class -- see the wiring there for why the
     thresholds differ. */
  .ax-head .ax-meta {
    overflow: hidden;
    max-height: 64px;
    transition: max-height var(--dur-panel) var(--ease), opacity var(--dur-panel) var(--ease);
  }
  .ax-head.is-compact .ax-meta { max-height: 0; opacity: 0; }
  /* A TRAILING ACTION BELONGS IN THE TITLE'S ROW, which is where Material
     puts it and where the three dots were already pointing. `display:
     contents` dissolves the group so its buttons become items of the header
     itself: the overflow and any icon action sit after the title, and the
     primary takes the row under them on its own. */
  .ax-head__act { display: contents; }
  .ax-head__act > .ax-head__more, .ax-head__act > .ax-btn--icon { order: 0; flex: 0 0 auto; }
  .ax-head__act > .ax-btn--primary { order: 1; flex: 1 0 100%; justify-content: center; }
  /* A BASIS OF ZERO IS WHAT MAKES IT SHARE. At `auto` the text block took
     the whole 343px row and pushed the overflow onto a line of its own: a
     header of four rows and 213px, worse than the three it replaced. */
  .ax-head__txt { flex: 1 1 0; min-width: 0; }
  .ax-sec__h { flex-wrap: wrap; row-gap: var(--space-15); }
  .ax-media__add { min-height: 44px; }
  /* A STEP YOU CAN HIT WITH A THUMB. Eleven of them at 10px tall is 10px of
     target, which the audit measures and refuses. On a phone the scale is a
     chunkier bar -- the same eleven steps, at the size a thumb needs. */
  .ax-scale__s { height: 28px; border-radius: var(--radius-sm); }
  .ax-scale__s.is-on { height: 34px; margin-top: -3px; }
  .ax-scale__s:hover { height: 28px; margin-top: 0; }
  /* NO HOVER HERE, SO THE MARKS STAY DRAWN. A control revealed by something
     that cannot happen is a control that is not there, and these two are the
     only way to take a photograph off or say what it shows. */
  .ax-media__x, .ax-media__cap svg { opacity: 1; }
  .ax-msg { padding: 11px 16px; }
  .ax-rail__i, .ax-nav__i, .ax-pop__i { height: 36px; }

  /* A thumb needs 28px on the short side, and three of the new controls were
     built for a mouse: the bare input inside a unit field, the calendar arrows
     and its footer. On a phone they grow; on a desktop they stay small. */
  .ax-bare { display: block; min-height: 28px; line-height: 28px; }
  .ax-val--unit { align-items: center; }
  .ax-pop__search { height: 34px; }
  .ax-pop__search input { min-height: 28px; }
  .ax-cal { width: 100%; }
  .ax-cal__nav { width: 32px; height: 32px; }
  .ax-cal__d { height: 34px; }
  .ax-cal__f button { height: var(--ctl-lg); padding: 0 var(--space-25); }
  /* the help icon is 14px; the target around it is not */
  .ax-tip__q { min-width: 28px; min-height: 28px; justify-content: center; margin: -7px; }
  .ax-nav__me { height: 40px; }
}
/* ============================================================================
   10 - WHEN IT IS WRONG
   The system knew empty and loading. It did not know failed, which is the
   state a console spends real time in.
   ========================================================================== */

/* This was an inset underline, on the argument that a mark is not a box. It
   was drawn as a 1.5px inset shadow, which follows the border-radius, so on a
   6px corner it curved up at both ends and read as a smile. The first person
   to use it said "el input field en error state tiene como una curva", which
   is exactly what it was.

   So: an error IS the one time a property draws a box. The field already
   carries a transparent 1px border for its focus state; being wrong colours
   it. That is the same shape of exception the system already makes for the
   second line -- an error you are meant to act on gets to break one rule. */
.ax-val.is-bad,
.ax-val.is-bad:hover { border-color: var(--danger-solid); }
.ax-val.is-bad:focus, .ax-val.is-bad:focus-visible {
  outline-color: var(--danger-solid);
  border-color: var(--danger-solid);
}

/* The real input has a real border, so it only changes colour. This rule was
   simply missing: the login field carried `is-bad` and nothing drew it. */
.ax-in.is-bad { border-color: var(--danger-solid); }
.ax-in.is-bad:focus {
  border-color: var(--danger-solid);
  box-shadow: 0 0 0 3px var(--danger-subtle);
  outline-color: var(--danger-solid);
}

.ax-err {
  display: flex;
  align-items: flex-start;
  gap: var(--space-1);
  margin-top: 3px;
  font-size: 11.5px;
  line-height: 15px;
  color: var(--danger-text);
}
.ax-err svg { flex: 0 0 auto; margin-top: 1px; color: var(--danger-solid); }

/* The warning is the same row in the other tone. It has to be a DIFFERENT
   colour from the error and not a lighter one: "saved, look at it" and
   "refused" are not two strengths of the same thing. */
.ax-err--warn { color: var(--warning-text); }
.ax-err--warn svg { color: var(--warning-solid); }

.ax-none--bad .ax-none__t { display: flex; align-items: center; gap: var(--space-15); }

/* A number over time. No axis, no grid: the two numbers that matter are set
   in type, and the line only has to carry the shape between them. */
.ax-hist { max-width: 260px; }
.ax-hist__top { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.ax-hist__v { font-size: 15px; font-weight: 600; letter-spacing: -0.012em; }
.ax-hist__d { font-size: 11.5px; color: var(--text-tertiary); }
.ax-hist__d.is-up { color: var(--success-text); }
.ax-hist__d.is-down { color: var(--danger-text); }
.ax-hist__g { display: block; margin: var(--space-15) 0 var(--space-05); color: var(--brand-solid); }
.ax-hist__ax { display: flex; font-size: 11px; color: var(--text-tertiary); }
.ax-hist__gap { flex: 1 1 auto; }

/* The presets sit ABOVE the calendar: four of five ranges anybody picks are
   named, and making them scroll past a month grid to reach "Last 30 days" is
   the calendar asserting an importance it does not have. */
.ax-rng { display: flex; flex-wrap: wrap; gap: var(--space-05); padding: var(--space-05); }
.ax-rng__i {
  flex: 1 1 calc(50% - 2px);
  height: 28px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: 12px;
  text-align: left;
  color: var(--text-secondary);
  background: none;
  border: 0;
  cursor: pointer;
}
.ax-rng__i:hover { background: var(--component-bg-hover); color: var(--text-primary); }
