:root {
  /* Native controls follow the page, not the OS. Declared once for the whole
     document rather than per component: without it a <select> or a disabled
     radio is drawn in the light theme on a dark page, and a disabled radio
     loses its selected dot — which role someone has must stay readable even
     when it cannot be changed. */
  color-scheme: dark;

  --bg: #0f1115;
  --panel: #181b22;
  --line: #262b35;
  --text: #e6e8ec;
  --muted: #8b93a3;
  --accent: #4f8cff;
  --green: #2fbf71;
  --red: #e5484d;
  --amber: #f5a623;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 2rem;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 ui-sans-serif, -apple-system, "Segoe UI", sans-serif;
}

h1 { font-size: 1.1rem; letter-spacing: .02em; text-transform: uppercase; color: var(--muted); margin: 2rem 0 .75rem; }
h1:first-child { margin-top: 0; }

code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em; color: var(--muted); }
.empty { color: var(--muted); }

/* Tables ------------------------------------------------------------------ */

.table { width: 100%; border-collapse: collapse; background: var(--panel); border-radius: 8px; overflow: hidden; }
.table th {
  text-align: left; font-size: .75rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); padding: .6rem .9rem; border-bottom: 1px solid var(--line);
}
.table td { padding: .6rem .9rem; border-bottom: 1px solid var(--line); }
.table tr:last-child td { border-bottom: 0; }
.table a { color: var(--accent); text-decoration: none; }
.table a:hover { text-decoration: underline; }

.pill { display: inline-block; padding: .15rem .5rem; border-radius: 999px; font-size: .75rem; background: var(--line); }
/* Two axes, and every pill below belongs to exactly one of them. This is the
   sentence that resolves --green "answer, live, available" against --amber
   "ringing, on call", which look like they overlap and do not:

     --green  CONNECTIVITY — this call is up, audio exists (En curso), or this
              person is ready for one (Disponible).
     --amber  AVAILABILITY — do not route here: nobody holds this call yet
              (Timbrando), or this person is occupied (En llamada).

   Amber's job on a crisis line is "somebody must act now". Spending it on a
   call that is already connected and fine is what empties it.

   Settled at L9's close, after the fix round briefly painted En curso amber:
   the console paints a live call green (evidence/phase_L9/10_*.png), so the
   same fact was green in one screen and amber in another. The labels these
   classes carry live in CallsHelper, never in the markup. */
.pill--available, .pill--live { background: rgba(47,191,113,.15); color: var(--green); }
/* Buzón rides the amber rule because amber is "somebody must act now": a
   voicemail is a caller waiting for a call back — the one row on Supervisión
   that still needs a human to do something. Red stays spent on Perdida /
   Fallida; a caller who was invited to leave a message is not the same
   failure as one the line dropped (node 2.2.4.4). */
.pill--on_call, .pill--ringing, .pill--voicemail { background: rgba(245,166,35,.15); color: var(--amber); }
/* No disponible — the person took herself off the rota (node 5.6). The purest
   instance of amber's own stated meaning above: AVAILABILITY, do not route
   here. Not red (red is spent on hang up, missed, failed, and taking a break
   is not a failure) and not muted (that is Desconectado — her browser stopped
   answering — and 5.6.2(d) exists to keep the two distinguishable). Full-value
   --amber on its tint, like its two neighbours: the .nav__absent comment below
   records that opacity over --muted composited to ≈2.8:1, so none is used. */
.pill--unavailable { background: rgba(245,166,35,.15); color: var(--amber); }
/* `no_answer` is the room model's older spelling of `missed` — the same fact,
   a call nobody picked up. It renders red beside it rather than muted, because
   a colour that depends on how old the document is would put the vintage of a
   record into a column a supervisor reads as meaning. */
.pill--missed, .pill--failed, .pill--no-answer { background: rgba(229,72,77,.15); color: var(--red); }
/* Completada is the ordinary outcome and not an alarm, so it is --muted like
   Desconectado and Desactivado: a table where every finished call shouts has
   no way left to say that one of them did not connect. */
.pill--completed, .pill--unknown-status { color: var(--muted); }

/* Softphone --------------------------------------------------------------- */

.softphone { max-width: 24rem; margin: 0 auto; }
.softphone__header { text-align: center; margin-bottom: 1.5rem; }
.softphone__header h1 { color: var(--text); text-transform: none; font-size: 1.4rem; margin: 0; }
/* Two lines are reserved, always. Every sentence this line can carry — from
   "Esperando llamadas" to "Tu sesión terminó. Inicia sesión otra vez para entrar
   a la llamada." to L5.20's two-sentence "Nadie contestó a tiempo. Se invitó a
   la persona a dejar un mensaje." (two lines at 390 px, one at 1440) — has to
   fit without growing the header, because the panel with Colgar in it sits
   directly underneath and a destructive control may not move under the pointer
   when the status changes. 13.21's rule: any sentence added here that needs a
   third line at 390 px must grow this reservation, not the element. */
.softphone__status { color: var(--muted); font-size: .85rem; margin: .25rem 0 0; min-height: 3em; }
.softphone__status.error { color: var(--red); }

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 1.5rem;
  text-align: center;
}
.panel--ringing { border-color: var(--amber); animation: pulse 1.4s ease-in-out infinite; }
/* A call this console holds whose ROOM IS NOT JOINED YET: neutral, deliberately,
   and it is the state every server render of a held call starts in. Green is the
   connectivity axis — "audio exists" — and the browser has not proved that yet,
   so painting it here would be the panel claiming a connection nobody made
   (UX.html, P1; the whole argument is in softphone/show.html.erb). Not amber
   either: amber is the availability axis, and it is already spent on the pulsing
   ringing panel one line above, which this state must not be mistaken for.
   The rule restates the default border on purpose — the state has a name so the
   stylesheet can change it and a test can assert it. */
.panel--connecting { border-color: var(--line); }
.panel--active { border-color: var(--green); }
.panel__hint { color: var(--muted); margin: 0; }
.panel__label { color: var(--muted); font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; margin: 0 0 .5rem; }
/* Which line is ringing. Smaller than the caller and above it: the number is
   what the agent reads out, the line is what tells them which protocol to
   answer with, and both have to be legible across a desk. Not muted grey —
   this one is read in the second before somebody picks up a crisis call. */
.panel__line { font-size: .95rem; margin: 0 0 .25rem; }
/* WHICH WAY THIS CALL WENT, in the accent token UX.html reserves for "links,
   outbound". It occupies the row an inbound call uses to name its line, so the
   panel has the same geometry in both directions — direction is carried by the
   word and by this colour, never by the panel's border, because the border
   answers "is this call up?" and that is the question whose wrong answer costs
   a caller. */
.panel__direction {
  color: var(--accent); font-size: .8rem; text-transform: uppercase;
  letter-spacing: .05em; margin: 0 0 .25rem;
}
.panel__caller { font-size: 1.5rem; font-variant-numeric: tabular-nums; margin: 0 0 1.25rem; }
.panel__actions { display: flex; gap: .5rem; justify-content: center; }

.btn {
  flex: 1; min-height: 40px; padding: .7rem 1rem; border: 1px solid var(--line);
  border-radius: 8px; background: var(--line); color: var(--text); font-size: .9rem;
  cursor: pointer;
}
.btn:hover { filter: brightness(1.2); }
/* A control that cannot be used must not look like one that can — and it must
   not look like a DIMMED version of itself either. A dimmed green Contestar
   still reads as "answer this call" (node 13.2.1, measured on L10's frame 10),
   and a dimmed red Colgar is the same defect on a destructive control, which is
   worse: a washed-out red is a colour a person reads as "danger, faintly". One
   neutral treatment, written once and inherited by every .btn variant, so a
   disabled control is grey whatever it was going to do. */
.btn:disabled, .btn:disabled:hover {
  opacity: .45; cursor: not-allowed; filter: none;
  background: var(--line); border-color: var(--line); color: var(--muted); font-weight: 400;
}
/* A RESERVED SLOT, NOT A DIMMED CONTROL (5.3.1.1). Silenciar is lit only while
   this console holds a room with a published microphone track; on every other
   panel the server draws it with this class — visibility and never display,
   because the flex row's geometry must be IDENTICAL in every state: removing
   the middle button moves the other two at the instant answered() runs, and a
   control sliding under the pointer that has just pressed Contestar is one
   click from a crisis call starting muted. Invisible is not merely disabled:
   the one button that can do nothing must not be the most prominent thing on a
   dead panel, which is exactly what its lit form was for six phases
   (phase_L4.17/09_silenciar_only_lit). visibility:hidden also removes it from
   the accessibility tree and the tab order, which `disabled` alone does not do
   for a screen reader announcing a control that cannot act. */
.btn--reserved, .btn--reserved:disabled { visibility: hidden; pointer-events: none; }

.btn--answer { background: var(--green); border-color: var(--green); color: #06210f; font-weight: 600; }
.btn--hangup { background: var(--red); border-color: var(--red); color: #2a0709; font-weight: 600; }
/* RECHAZAR — the red button beside Contestar on a ringing panel, and the ONLY
   block in this file that styles it (13.25: a duplicate selector further down
   once reverted a fix that had already passed review, silently).

   It is --red like Colgar, from the token and never a literal, and the sameness
   is the decision rather than an accident: in Aircall the red control in this
   position is Decline, Picap's nine agents are coming off Aircall, and the
   defect this phase fixes is that the button they have pressed a thousand times
   meant "hang up on the caller" here. The picture an agent brings with her stays
   the same picture; what changes is what the press does. It is also the palette's
   own reading — --red is "hang up, missed, failed", and a declined call nobody
   takes is exactly a missed one.

   Distinct from .btn--hangup by NAME so that the two acts cannot be confused in
   a template or a test, and so the day the two ever diverge visually there is
   already a rule to change. */
.btn--decline { background: var(--red); border-color: var(--red); color: #2a0709; font-weight: 600; }

.btn--primary { background: var(--accent); border-color: var(--accent); color: #04122e; font-weight: 600; }

/* Dialer ------------------------------------------------------------------ */

/* Below "Sin llamadas en curso." and never beside it: the idle sentence stays,
   because state is never inferred from absence, and the keypad is a second
   thing rather than the answer to it. */
.panel--dialer { margin-top: 1rem; text-align: left; }
.dialer__label {
  display: block; color: var(--muted); font-size: .8rem; text-transform: uppercase;
  letter-spacing: .05em; margin-bottom: .35rem;
}
.dialer__input {
  width: 100%; box-sizing: border-box;
  padding: .7rem .8rem; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg); color: var(--text); font-size: 1.25rem;
  font-variant-numeric: tabular-nums; text-align: center;
}
.dialer__input:focus { outline: none; border-color: var(--accent); }
/* Twelve keys in three columns, the shape of every telephone anybody has ever
   used. Fixed rows so the panel's height does not depend on the glyphs. */
.dialer__keys {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; margin: .75rem 0;
}
.dialer__key { min-height: 44px; font-size: 1.05rem; font-variant-numeric: tabular-nums; }
.dialer__call { width: 100%; }
.dialer__hint { color: var(--muted); font-size: .85rem; margin: .6rem 0 0; text-align: center; }
/* Which number the customer will see, stated before the call is placed. Quiet —
   it is a fact to be read, not a warning — and it is the only guard L12 has
   against the caller ID being chosen silently in code. */
.dialer__caller {
  color: var(--muted); font-size: .8rem; margin: .75rem 0 0; text-align: center;
}

.pill--outbound { background: rgba(79,140,255,.15); color: var(--accent); }
.pill--dialing { background: rgba(245,166,35,.15); color: var(--amber); }

/* Nav and flash --------------------------------------------------------- */

/* A SIDEBAR, and the markup did not change to get one.
   `nav.nav`, `.nav__who`, `.nav__links`, `.nav__signout` and `.nav__absent` are
   asserted by seventeen suites — several of them sealed — so the element tree
   is exactly what it was and only this stylesheet moved. That is also why the
   page did not become a grid: `body` stays the page, the sidebar is fixed, and
   the content column is given room by a margin rather than by a track. A layout
   change that renames a class is a layout change that breaks an archive.

   Why a sidebar at all: Picap's people read Aircall's left rail every day, and
   the whole argument for borrowing its order was that somebody moving between
   the two products should not have to hunt. Same order, same side. */
.nav {
  position: fixed; top: 0; left: 0; bottom: 0;
  width: 14rem;
  display: flex; flex-direction: column;
  gap: .75rem;
  padding: 1.25rem 1rem;
  margin: 0;
  background: var(--panel);
  border-right: 1px solid var(--line);
  border-bottom: 0;
  overflow-y: auto;
  z-index: 10;
}
/* The person and their account, at the top where Aircall puts the account and
   where a reader looks first to answer "who am I signed in as". */
.nav__who {
  color: var(--muted); font-size: .85rem;
  padding-bottom: .75rem; border-bottom: 1px solid var(--line);
}
/* THE AVAILABILITY BLOCK (node 5.6): the person's own pill, the control that
   flips it, and a note line — in the identity cluster, deliberately OUTSIDE
   .nav__links, because below 900 px the links are display:none behind Menú and
   this control must survive the collapse. Being outside .nav__links it does NOT
   inherit `.nav__links > *`'s 40 px pitch, so the control sets its own floor
   below. */
.nav__availability {
  display: flex; flex-direction: column; align-items: flex-start; gap: .4rem;
  padding-bottom: .75rem; border-bottom: 1px solid var(--line);
}
/* UX.html's 40 px floor for a click target's short side, set here because
   nothing is inherited from the links' pitch rule (see the block comment). */
.nav__availability-toggle {
  min-height: 40px; padding: .5rem .75rem;
  background: none; border: 1px solid var(--line); border-radius: 6px;
  color: var(--text); font-size: .85rem; cursor: pointer; text-align: left;
}
.nav__availability-toggle:hover { background: var(--line); }
.nav__availability-toggle:disabled {
  color: var(--muted); cursor: not-allowed; background: none;
}
/* THE NOTE'S SPACE IS RESERVED AT ALL TIMES, EMPTY OR NOT — 13.21, and this
   phase's sharpest requirement. At ≤900 px the rail is a block ABOVE the
   content, so a line appearing here would push the console — Contestar and
   Rechazar included — down at the exact instant the agent is pressing one of
   them. The reservation is MEASURED AT EACH RAIL WIDTH, because the rail has
   two of them and the longest sentence wraps differently in each:

     desktop, fixed rail, 12rem of content: the two longest of the ten
       sentences wrap to THREE lines (measured 57.56 px at L4.17's review
       against a 38.4 px reservation, pushing .nav__links down 19.18 px —
       moving link targets under the pointer), so the floor here is 4.5em
       (three lines at the element's own .8rem and the page's 1.5 line-height);
     ≤900 px, full-width block (326 px at 390): every sentence fits TWO lines,
       so the media block below restores 3em — the exact reservation the
       0.00 px numbers in test/system/availability_evidence_test.rb were
       measured against, byte-identical.

   Revealing, changing or clearing the note must move the console's actions
   row (at 390) and the menu links (at desktop) by 0.00 px, asserted against
   numbers rather than eyeballed, in that same suite. */
.nav__availability-note {
  margin: 0; color: var(--muted); font-size: .8rem; min-height: 4.5em;
  /* Full width of the rail, against the parent's flex-start: the reserved box
     must be the SAME box empty and full, or the reservation is only a height
     claim while the width jumps with every sentence. */
  align-self: stretch;
}

/* The destinations stack, and Salir is pushed to the floor of the rail by the
   auto margin below — always in the same place, never moving as the list above
   it grows or shrinks with the person's role. */
.nav__links {
  display: flex; flex-direction: column; align-items: stretch;
  gap: .1rem; flex: 1;
}
/* 40 px minimum pitch: UX.html's floor for a click target's short side, and a
   block row's hit area cannot exceed its own pitch. */
.nav__links > * { padding: .5rem; border-radius: 6px; min-height: 40px; display: flex; align-items: center; }

/* THE PIN IS ON THE FORM, NOT ON THE BUTTON, and that is the whole bug.
   `button_to` wraps its button in a <form>, so the form is the flex child and
   `margin-top: auto` on `.nav__signout` was set on an element the flex
   container never sees. The first version of this rail claimed Salir was pinned
   to the floor and it was not: it sat under the last item and moved with the
   person's role, which is exactly what the pin exists to prevent. */
/* The form is a layout wrapper and nothing else: it must not add its own inset,
   or Salir hangs 8 px left of every row above it (the anchors' padding lives on
   the row, and the button is a grandchild). */
.nav__links > form { margin-top: auto; padding: 0; width: 100%; }
.nav__signout {
  /* --text, not --muted. Seven consecutive grey rows teach the eye that grey
     means "does not exist yet", and the exit was wearing that same grey — the
     only way out of the product read as the eighth dead row, most dangerously
     on the password-pending screen where it is the ONLY way out. */
  color: var(--text); text-align: left; width: 100%;
  min-height: 40px; padding: .5rem; border-radius: 6px;
  border-top: 1px solid var(--line); margin-top: auto;
}
.nav__signout:hover { color: var(--text); background: var(--line); }

/* The content column. The sidebar is fixed, so nothing in the flow knows it is
   there; this is what gives the page its room back. `body` keeps its 2rem, so
   every existing page's own spacing is untouched. */
.shell { margin-left: 14rem; }
/* Signed out there is no rail, so the sign-in page keeps the whole width and
   does not sit in a column beside nothing. */
.shell--bare { margin-left: 0; }

/* The toggle exists in the DOM at every width and is only ever SEEN on a
   telephone: hiding it with display:none above 900 keeps it out of the tab
   order on a desktop, where the rail is always open and a button that toggles
   nothing is a control that lies. */
.nav__toggle {
  display: none;
  background: none; border: 1px solid var(--line); border-radius: 6px;
  color: var(--text); font-size: .85rem; cursor: pointer;
  padding: .5rem .75rem; min-height: 40px; text-align: left;
}
.nav__toggle:hover { background: var(--line); }

/* NARROW: the rail stops being fixed and becomes a block at the top of the page,
   and it STAYS A COLUMN.
   The first version turned it back into a wrapped row here, and on an actual
   telephone that is not a menu — the identity line floated left of the links,
   the twelve destinations wrapped into ragged two-up rows, and the inert items
   read as boxes because a row of items with padding and a radius is a row of
   chips. A list that is legible at 1440 does not become legible by being folded.
   Same order, same treatments, one column, full width. */
@media (max-width: 900px) {
  .nav {
    position: static; width: auto; height: auto;
    flex-direction: column; align-items: stretch;
    border-right: 0; border-bottom: 1px solid var(--line);
    margin: -1rem 0 1.5rem; padding: 0 0 .75rem;
    background: none; overflow: visible;
  }
  .nav__who { border-bottom: 1px solid var(--line); padding: 0 0 .5rem; margin-bottom: .25rem; }
  /* Two lines are enough at this width — the rail is full width here, and the
     longest of the ten sentences wraps to two lines at 326 px (measured at a
     true CDP 390 in availability_evidence_test.rb). This is the 3em every
     0.00 px number in that suite was taken against; only the DESKTOP floor
     grew to 4.5em (see the note's own comment). 13.25: this is the one
     deliberate second occurrence of the selector, and it is inside this media
     block on purpose. */
  .nav__availability-note { min-height: 3em; }
  .nav__links { flex-direction: column; align-items: stretch; gap: .1rem; flex: none; }
  .nav__links > form { margin-top: .25rem; }
  .shell { margin-left: 0; }

  /* THE COLLAPSE, and the whole reason for it: with twelve destinations open,
     the softphone sat below the fold on a telephone. An agent's job is the
     softphone; discovery happens once and answering happens all day, so the
     list starts closed and the tool is the first thing on screen.
     `display: none` and not a height animation — a hidden menu must be out of
     the tab order too, or a person tabbing lands in a drawer they cannot see. */
  .nav__toggle { display: block; align-self: flex-start; margin: .5rem 0 0; }

  /* ONLY A COLLAPSIBLE RAIL COLLAPSES, and the bug that earned this line is the
     one worth remembering: with a password change pending the toggle is not
     rendered, because there is nothing to open — but Salir lives inside the
     panel, so collapsing it left the ONE way out of that screen invisible with
     no button anywhere to reveal it. On a telephone the person was trapped, on
     the exact screen the whole suppression rule exists to keep them free of. */
  .nav--collapsible .nav__links { display: none; }
  .nav--collapsible[data-open="true"] .nav__links { display: flex; }
}
.nav__links a { color: var(--accent); text-decoration: none; font-size: .85rem; }
.nav__links a:hover { text-decoration: underline; }

/* The page you are on, named rather than merely coloured: --text is the same
   weight the rest of the bar uses, so "here" reads as emphasis and not as a
   second kind of link. */
.nav__links a.is-here { color: var(--text); font-weight: 600; text-decoration: none; cursor: default; }

/* AN UNBUILT DESTINATION, AND EVERY PROPERTY HERE IS DELIBERATE.
   It is --muted, so it does not read as a link beside real ones; it has NO
   hover state and NO pointer cursor, because a thing that responds to a pointer
   is a thing a person will press; and `default` is set explicitly rather than
   inherited, so a future rule on .nav__links cannot quietly give it one back.
   The sentence lives in the title attribute — the item names the absence, and
   nowhere does it promise a date. If this ever gains a hover, a cursor or a
   "pronto", the shell has started lying and the rule in ShellHelper is broken. */
.nav__absent {
  /* Full --muted, no opacity: at .55 this composited to ≈2.8:1 and UX.html's
     floor for TEXT is 4.5:1. The shell's own argument is that this is a
     statement and not a control, so it takes the text bar, and an absence
     nobody can read is not a statement. */
  color: var(--muted);
  font-size: .85rem;
  user-select: none;

  /* WHAT SEPARATES IT FROM SALIR IS NOT COLOUR, AND THAT IS THE POINT.
     Raising the contrast put it at exactly --muted, which is what .nav__signout
     already is — so a person who had learned "grey goes nowhere" would meet
     grey at the exit. The dotted underline and the help cursor are the
     conventional signal for "there is an explanation here", they are NOT link
     or button affordances, and they are the only thing on this bar that invites
     the hover that reveals the sentence. Before this, the copy lived in a title
     attribute nothing asked anybody to look for.

     currentColor, NOT --line: the first attempt drew this rule in the divider
     token, which is ≈1.34:1 against the background — a signal specified in a
     value that cannot survive its own backdrop, and invisible in every frame.
     The convention being borrowed is the browser's own `abbr[title]`, and that
     inherits the TEXT colour. Tying it to currentColor also means it can never
     drift from the label it underlines. */
  border-bottom: 1px dotted currentColor;
  cursor: help;
  text-decoration: none;

  /* NO RADIUS ON THIS ONE. `.nav__links > *` rounds every row so a hover or the
     current page gets a soft background — but a bottom border under a rounded
     box curves up at both ends, and those two little hooks read as a box drawn
     around the label. On a telephone, where the same rule is proportionally
     larger, it reads as a disabled field. The annotation has to be a line under
     a word, not three sides of a container. */
  border-radius: 0;

  /* HUG THE WORD. In the horizontal bar this rule sat under each label and read
     as an annotation; stacked in a 14rem column the same rule ran the full width
     of every row and read as a table divider — worse, it fenced the rail into
     sections that mean nothing, because live items carry no rule. Shrinking the
     item to its content restores the treatment that was cleared. */
  align-self: flex-start;
}
.nav__absent:hover { text-decoration: none; color: var(--muted); }
/* The exit's own colour lives with the rail, ~90 lines above: this block used to
   restate it as --muted and, being later in the file, quietly won. That is how
   the "Salir is not the eighth dead row" fix passed review while never taking
   effect — a duplicate selector further down is a fix that reports success. */
.nav__signout { background: none; border: 0; font-size: .85rem; cursor: pointer; }

.flash { padding: .6rem .9rem; border-radius: 8px; margin: 0 0 1rem; font-size: .9rem; }
.flash--alert { background: rgba(229,72,77,.15); color: var(--red); }
.flash--notice { background: rgba(47,191,113,.15); color: var(--green); }

/* Sign in ----------------------------------------------------------------- */

.auth { max-width: 20rem; margin: 6rem auto 0; }
.auth__title { color: var(--text); text-transform: none; font-size: 1.6rem; text-align: center; margin: 0 0 1.5rem; }
.auth__form { display: flex; flex-direction: column; gap: .9rem; }

.field { display: flex; flex-direction: column; gap: .3rem; }
.field__label { font-size: .8rem; color: var(--muted); }
.field__input {
  padding: .6rem .7rem; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); color: var(--text); font-size: .95rem;
}
.field__input:focus { outline: none; border-color: var(--accent); }

@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(245,166,35,.35); }
  50%      { box-shadow: 0 0 0 .6rem rgba(245,166,35,0); }
}

/* Supervisor dashboard ---------------------------------------------------- */

.page-head { display: flex; align-items: baseline; justify-content: space-between; }
h2 { font-size: .95rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 2rem 0 .75rem; }

.range { display: flex; gap: .3rem; }
.range__opt {
  padding: .25rem .6rem; border-radius: 6px; font-size: .8rem;
  color: var(--muted); text-decoration: none; border: 1px solid var(--line);
}
.range__opt:hover { color: var(--text); }
.range__opt--on { background: var(--line); color: var(--text); }

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: .75rem; }
.stat { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 1rem 1.1rem; }
.stat__label { font-size: .75rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 0; }
.stat__value { font-size: 1.9rem; font-variant-numeric: tabular-nums; margin: .3rem 0 .15rem; }
.stat__sub { font-size: .78rem; color: var(--muted); margin: 0; }
.stat--good .stat__value { color: var(--green); }
.stat--warn .stat__value { color: var(--amber); }

.table .num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* Teams ------------------------------------------------------------------- */

.team__title { color: var(--text); text-transform: none; font-size: 1.4rem; margin: 0; }
.team__counts { color: var(--muted); font-size: .85rem; margin: .25rem 0 0; }

/* A team with members and nobody available: as dead as an empty one on a crisis
   line, and today the only thing that says so is a 0 in a numeric column, which
   reads as data rather than as an alarm (node 1.7.4).

   AMBER and not red, and the axis is the one L9's close ruled: green is
   CONNECTIVITY, amber is AVAILABILITY. Red is spent on hanging up, missing and
   failing — nothing has failed here, and nobody has missed anything yet. That
   is the whole point of the warning: it is seen BEFORE a caller finds out. */
.warning {
  background: rgba(245,166,35,.10); border: 1px solid rgba(245,166,35,.35);
  color: var(--amber); border-radius: 8px; padding: .6rem .9rem;
  margin: 0 0 1rem; font-size: .9rem;
}
.pill--unstaffed { background: rgba(245,166,35,.15); color: var(--amber); }

.pill--offline { color: var(--muted); }
/* Grey, exactly like Desconectado: node 13.4 records that this roster already
   spends amber and green on two different meanings, and red is reserved for
   hanging up, missing and failing. Deactivated is not a failure. */
.pill--deactivated { color: var(--muted); }

/* A link that has to look and click like a button. */
a.btn, .btn--quiet { display: inline-block; text-decoration: none; text-align: center; flex: 0 0 auto; }
.btn--quiet { background: transparent; color: var(--muted); }
.btn--quiet:hover { color: var(--text); }
.btn--danger { background: var(--red); border-color: var(--red); color: #2a0709; font-weight: 600; }

.panel--empty { display: flex; align-items: center; justify-content: space-between; gap: 1rem; text-align: left; }
.panel--form { max-width: 30rem; text-align: left; }

.form { display: flex; flex-direction: column; gap: .9rem; }
.form--inline { flex-direction: row; align-items: flex-end; gap: .6rem; }
.form--inline .field { flex: 1; }
.form__actions { display: flex; gap: .6rem; }
/* Node 1.2.2.9. A submit sizes to its label, like every other button in liza.
   `.btn` carries flex: 1 for the softphone's two-button row, where Contestar and
   Colgar must split the panel between them; a form's actions are not that row,
   and the user detail — which now carries two submits — would otherwise stack
   two full-width bars where every other screen has two buttons side by side.
   The fix is in the shared rule because the defect was in the design system. */
.form__actions .btn { flex: 0 0 auto; }
.field__hint { color: var(--muted); font-size: .75rem; margin: 0; }
.field__error { color: var(--red); font-size: .8rem; margin: 0; }
/* One save can break two invariants at once — lowering the last owner's role
   also empties the administrator seat — and two sentences packed against each
   other read as one wrapped paragraph. */
.field__error + .field__error { margin-top: .4rem; }
.field__input--glyph { font-size: 1.2rem; max-width: 5rem; }

.table__actions { text-align: right; width: 1%; white-space: nowrap; }
.table__actions form { display: inline; }

.danger { margin-top: 3rem; border: 1px solid var(--red); border-radius: 12px; padding: 1.25rem 1.5rem; max-width: 30rem; }
.danger__title { color: var(--red); margin: 0 0 .5rem; }
.danger__form { margin-top: 1rem; }

/* Users ------------------------------------------------------------------- */

.pill--role-agent { color: var(--muted); }
.pill--role-supervisor { background: rgba(79,140,255,.15); color: var(--accent); }
.pill--role-admin { background: rgba(47,191,113,.15); color: var(--green); }
.pill--owner { background: rgba(245,166,35,.15); color: var(--amber); }

.user__title { color: var(--text); text-transform: none; font-size: 1.4rem; margin: 0; }
.user__self { color: var(--muted); font-size: .85rem; }
.user__email { color: var(--muted); font-size: .85rem; margin: .25rem 0 0; }

.panel--roles { max-width: 38rem; text-align: left; }
.panel--stacked { margin-top: 1rem; }

/* Información personal, the first block on the General tab and the same width as
   the two below it. The gap is what keeps three stacked forms from reading as
   one long form with three buttons in it. */
.panel--identity { max-width: 38rem; text-align: left; margin-bottom: 1rem; }

/* A section title inside a card. The bare h2 rule carries a 2rem top margin
   meant for page sections, which is wrong once the title is inside a panel. */
.panel__title {
  margin: 0 0 1rem; font-size: .8rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted);
}

/* Configuración del perfil ------------------------------------------------ */

/* The Idioma value is a fact, not a control, so it is text at reading size and
   is deliberately not marked up as a labelled input. */
.profile__value { margin: 0; font-size: .95rem; }

/* The two rows of the block are separated the way Rol and Permisos avanzados
   are, so "read-only fact" and "editable field" do not read as one paragraph. */
.form--profile { margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--line); }

/* Three digits are an identifier, not a quantity: tabular figures, left
   aligned, at a size that can be read across a desk. */
.field__input--extension {
  max-width: 6rem; font-size: 1.2rem; letter-spacing: .12em;
  font-variant-numeric: tabular-nums;
}

/* Node 13.8: the hint carrying the only explanation of a field must not be the
   smallest type on the page. */
.field__hint--lead { font-size: .85rem; max-width: 34rem; }

/* Tab strip: two real URLs, and the active one is marked by more than colour
   (the link carries aria-current="page"). Click targets stay ≥ 40px tall. */
.tabs { display: flex; gap: .25rem; margin: 1.25rem 0 .5rem; border-bottom: 1px solid var(--line); }
.tabs__link {
  display: inline-flex; align-items: center; min-height: 40px; padding: 0 .9rem;
  color: var(--muted); text-decoration: none; font-size: .9rem;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabs__link:hover { color: var(--text); }
.tabs__link--active { color: var(--text); border-bottom-color: var(--accent); }

/* The outbound identity of a person, in the token UX.html assigns to outbound —
   not green (answer / live) and not amber (ringing / on call). */
.pill--default { background: rgba(79,140,255,.15); color: var(--accent); }

/* Phone numbers are identifiers, not quantities: tabular figures so a column
   lines up, but left-aligned — the right-alignment rule is about durations. */
.table .phone, th.phone { text-align: left; font-variant-numeric: tabular-nums; }

.block { border: 0; padding: 0; margin: 0; }
.block--perms { margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--line); }
.block__title {
  padding: 0; margin-bottom: .5rem; font-size: .8rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted);
}

.role {
  display: grid; grid-template-columns: 1.1rem 1fr; column-gap: .7rem;
  align-items: center; padding: .5rem 0; min-height: 40px;
}
.role__input { width: 1.1rem; height: 1.1rem; accent-color: var(--accent); }
.role__name { color: var(--text); font-size: .95rem; cursor: pointer; }
.role__input:checked ~ .role__name { font-weight: 600; }
.role__input:disabled { cursor: not-allowed; }
.role__input:disabled ~ .role__name { color: var(--muted); cursor: not-allowed; }
.role__scope { grid-column: 2; color: var(--muted); font-size: .8rem; margin: .15rem 0 0; }

/* The roster ---------------------------------------------------------------
   The banner and the filter bar sit above a full-width table, so both are
   full-width too: the narrow .panel--form box reads wrong there (node 13.6). */

.banner {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  margin: 0 0 1rem; padding: .9rem 1.1rem; border-radius: 10px;
  /* Not an error colour: an account with no owner has not failed at anything,
     it has simply never named one. */
  background: rgba(79,140,255,.12); border: 1px solid var(--line);
}
.banner__text { margin: 0; font-size: .9rem; }

.filters {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: .75rem;
  margin: 0 0 1rem; padding: .9rem 1.1rem; border-radius: 10px;
  background: var(--panel); border: 1px solid var(--line);
}
.filters .field { flex: 1 1 12rem; }
.filters__actions { display: flex; gap: .5rem; margin-left: auto; }

.field__select {
  min-height: 40px; padding: .5rem .7rem; border: 1px solid var(--line);
  border-radius: 8px; background: var(--panel); color: var(--text); font-size: .95rem;
}
.field__select:focus { outline: none; border-color: var(--accent); }

/* Creating a person, and the one time its password is on screen. */
.form__note { color: var(--muted); font-size: .85rem; margin: 0; }

.created__password {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.6rem; letter-spacing: .08em; margin: 0 0 1.25rem;
}
.created__warning { font-size: .9rem; margin: 0 0 .75rem; }

.form__locked {
  margin: 0 0 1rem; padding: .6rem .9rem; border-radius: 8px; font-size: .85rem;
  background: rgba(245,166,35,.12); color: var(--amber);
}

/* Credenciales, and the two screens the reset is spread over ---------------
   The block is the last one on the General tab and carries the same gap the
   other stacked blocks do. It deliberately does NOT reuse .panel--stacked:
   two suites find that class expecting exactly one match. */
.panel--credentials { margin-top: 1rem; }

/* Whether the hand-over landed. It sits under the block's own explanatory
   sentence and is a different fact about a different moment, so it gets the air
   that says so — packed against the paragraph above it, it reads as its last
   line. */
.credentials__pending { margin-top: .6rem; }

/* A person's own password, at /password (node 1.2.4.3). The only rule the screen
   needs: .field__hint carries margin: 0 because it normally sits *inside* a
   .field, packed against its own input. The intro and the sentence explaining
   why the three boxes came back empty are prose ABOVE the form, and without air
   the first field's label reads like the last line of the paragraph — on the one
   screen in liza where the reader has just been refused and is looking for the
   reason. No colour, no new token. */
.panel--password > p.field__hint { margin-bottom: 1rem; }

.confirm__lead { margin: 0 0 1.25rem; font-size: .95rem; }
.confirm__list { margin: 0 0 1.5rem; padding-left: 1.1rem; font-size: .95rem; }
.confirm__list li { margin: .35rem 0; }

/* The in-page confirmation (node 5.14) -------------------------------------
   A closed <dialog> is display: none by the UA stylesheet, so this component
   OVERLAYS and reserves no space in the flow: the page behind it is
   pixel-identical open and closed. That is binding rather than aesthetic —
   liza's flash row displaces the call panel by 56.8 px (node 13.21) and L7 spent
   a fix round on a status line that pushed Colgar 20.39 px under an agent's
   pointer, and this component is the one topic 5 will put beside Colgar. */
.confirm__dialog {
  max-width: 32rem; padding: 1.5rem; color: var(--text);
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
}
.confirm__dialog::backdrop { background: rgba(15, 17, 21, .72); }
.confirm__title {
  margin: 0 0 1rem; font-size: 1.05rem; text-transform: none;
  letter-spacing: 0; color: var(--text);
}
.confirm__note { margin: 0 0 1.5rem; font-size: .9rem; color: var(--muted); }
/* A sentence that is only sometimes true — here, that the person is on a call
   right now. Amber and not red: nothing has failed, and the call is not going to
   be cut. Same treatment as the roster's locked notice. */
.confirm__warning {
  margin: 0 0 1.25rem; padding: .6rem .9rem; border-radius: 8px; font-size: .9rem;
  background: rgba(245,166,35,.12); color: var(--amber);
}
.confirm__actions { display: flex; gap: .6rem; }
/* Buttons size to their labels here too — the shared .btn carries flex: 1 for
   the softphone's two-button row and a dialog is not that row (node 1.2.2.9). */
.confirm__actions .btn { flex: 0 0 auto; }

/* The console whose session has ended. A notice, not an error: nothing has gone
   wrong with the call, and the call is still up. Same treatment as the roster's
   banner, and for the same reason — nobody has failed at anything.

   The margin is BELOW-first because the notice renders below the call panel: it
   appears without moving a single pixel of the panel above it. */
.notice {
  margin: 1rem 0 0; padding: .9rem 1.1rem; border-radius: 10px; text-align: left;
  background: rgba(79,140,255,.12); border: 1px solid var(--line);
}
.notice__text { margin: 0 0 .75rem; font-size: .9rem; }

/* Daily volume ------------------------------------------------------------ */

.chart { margin: 0; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 1rem; }
.chart__bars { display: flex; align-items: flex-end; gap: 2px; height: 9rem; }
.chart__col { flex: 1; height: 100%; display: flex; align-items: flex-end; }
.chart__stack { width: 100%; min-height: 2px; display: flex; flex-direction: column; border-radius: 3px 3px 0 0; overflow: hidden; }
.chart__seg--in { background: var(--green); }
.chart__seg--out { background: var(--accent); }
.chart__col:hover .chart__stack { filter: brightness(1.3); }

.chart__legend { display: flex; gap: 1rem; align-items: center; margin-top: .7rem; font-size: .78rem; color: var(--muted); }
.chart__range { margin-left: auto; }
.swatch { display: inline-block; width: .6rem; height: .6rem; border-radius: 2px; margin-right: .3rem; }
.swatch--in { background: var(--green); }
.swatch--out { background: var(--accent); }
