/* store_v1 — admin panel.
   Same visual family as the catalogue (navy blue + light surfaces), with the dark top bar of
   the Pilnook panels. */

:root {
  /* The same ones as the catalogue in production (see public.css). */
  --navy: #0a2540;
  --navy-light: #13385f;
  --navy-soft: #1c4c7c;
  --dark: #0a2540;
  --ink: #0a2540;
  --text-main: #2b3440;
  /* 🔴 THESE TWO WERE DARKENED BECAUSE THEY WERE UNREADABLE, and the number says it better than
     the eye. Over the grey `--bg` background they sat at 4.26 and 1.99 contrast, with the legible
     minimum at 4.5: the faint text was literally HALF of what is needed. It is used in per-unit
     prices, basket notes and form help — places where putting something unreadable is worse than
     putting nothing, because it takes the slot of the explanation.

     Now 5.38 and 4.62 over grey, and the hierarchy holds: main very dark, soft, faint. There is a
     test that measures this; if someone lightens them again, it fails. */
  --text-soft: #5b6675;
  --text-faint: #667080;
  --bg: #f4f6f9;
  --surface: #ffffff;
  --surface-2: #f8fafc;
  --border: #e4e8ee;
  --border-2: #cfd6e0;
  --success: #1f9254;
  --success-bg: #eaf7f0;
  --warning: #b26a00;
  --warning-bg: #fdf4e5;
  --error: #c62828;
  --error-bg: #fdecec;
  --radius: 10px;
  --shadow: 0 1px 2px rgba(16, 33, 58, .06);
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: "SF Mono", "Cascadia Code", "Fira Code", ui-monospace, monospace;
  /* Soft tints and inks of the flat pages (rc000365: they were literals repeated ~30 times). */
  --tint-blue: #eaf1fb;
  --ok-ink: #1f7a48;
  --tint-ok: #e8f6ee;
  --warn-ink: #b26a00;
  --tint-warn: #fdf4e5;
  --warn-dark: #92400e;
}

* { box-sizing: border-box; }
html { font-size: 16px; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text-main);
  font-family: var(--font);
  line-height: 1.45;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

a { color: var(--navy-soft); text-decoration: none; }
h1, h2, h3 { color: var(--ink); }

/* 🔧 The top bar (`.topbar*`) and the user menu (`.user-dd`, `.dd-*`) were MOVED to
   `static/css/chrome.css`, which both this panel and the logged-in customer's store load: one
   design for the two shells. Only the panel's own pieces remain here (e.g. `.login-logo`). */

.login-logo {
  max-width: 180px;
  height: 66px;
  margin: 0 auto 4px;
  display: block;
  object-fit: contain;
}

/* ---------- Content ---------- */

.content { flex: 1; width: 100%; max-width: 1360px; margin: 0 auto; padding: 24px; }

.section-head {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}

.section-head h1 { margin: 0; font-size: 21px; }
.section-head .sub { margin: 0; color: var(--text-soft); font-size: 14.5px; }
.section-head .btn-primary,
.section-head .more-link { margin-left: auto; }

.more-link { color: var(--navy-soft); font-size: 14.5px; font-weight: 600; text-decoration: none; }
/* Tone, not underline: see the `.filters-clear` comment in public.css. */
.more-link:hover { color: var(--accent); }

/* ---------- Cards and tables ---------- */

.panel-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 20px 22px;
  margin-bottom: 20px;
}

.panel-card h2 { margin: 0 0 14px; font-size: 16.5px; }
.panel-card h3 { margin: 16px 0 6px; font-size: 14.5px; }

.panel-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 12px;
}

.panel-card-head h2 { margin: 0; }

.table-wrap { overflow-x: auto; margin: 0 -22px; padding: 0 22px; }

.table { width: 100%; border-collapse: collapse; font-size: 14.5px; }

.table th {
  text-align: left;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-soft);
  font-weight: 700;
  background: var(--surface-2);
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.table td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.table tbody tr:hover { background: var(--surface-2); }
.table tfoot td { border-bottom: 0; padding-top: 12px; }
/* A group of lines inside a document's table: the department whose lines follow (rc000445). It is a row of
   the body, not a second header: lighter than the column heads, and it must not inherit their dark fill. */
.table tbody .group-row th {
  background: var(--tint-blue); color: var(--text-main); text-align: left;
  font-size: 12.5px; font-weight: 700; letter-spacing: .03em; text-transform: none;
  padding: 7px 12px; border-bottom: 1px solid var(--border);
}
.table .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.table .muted { color: var(--text-soft); }
.table a { text-decoration: none; }
.table a:hover { color: var(--accent); }

.cell-actions { white-space: nowrap; text-align: right; }
.row-error { background: var(--error-bg); }
.row-error-detail { color: var(--error); font-size: 13px; }

.row-form { display: flex; gap: 6px; align-items: center; }

/* 🔴 The product names run to 70 characters, and a <select> sizes itself to its longest option:
   without a ceiling the «Asignar a» column pushed the media table past the width of the screen.
   The cap goes on the control, not on the table — `text-overflow` does not apply to a select, so
   the browser clips the visible label while the dropdown itself still shows the whole name. */
.row-form select {
  padding: 5px 8px;
  font-size: 13.5px;
  max-width: 230px;
  min-width: 0;
}
/* 🔴 A loose input in a row (e.g. rename) does NOT inherit the `.field input` style: without this
   it comes out with the browser's default border and clashes with the rest of the panel. It gets the
   same look (border, radius, focus) but at row size. */
.row-form input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]) {
  border: 1px solid var(--border-2);
  border-radius: 8px;
  padding: 7px 11px;
  font: inherit;
  font-size: 14px;
  color: var(--text-main);
  background: #fff;
  max-width: 260px;
  min-width: 0;
}
.row-form input:focus {
  outline: none;
  border-color: var(--navy-soft);
  box-shadow: 0 0 0 3px rgba(29, 79, 134, .12);
}

/* Buttons and short labels never break mid-word: `overflow-wrap: anywhere` on the whole table
   turned «Ficha» into «Fich / a» and «Asignar» into «Asign / ar». Long file names are already
   trimmed by `shorten` before they get here, so ordinary wrapping is enough. */
.table .btn-mini,
.table .status,
.table .num,
.table .cell-actions,
.table .nowrap { white-space: nowrap; }


/* Bulk-archive bar above the pending queue. */
.bulk-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-bottom: 12px; }
/* The bulk bar of the product listing has a label and a <select> (rc000445): the house field box. */
.bulk-bar label { font-size: 13.5px; font-weight: 600; color: var(--text-main); }
.bulk-bar select { max-width: min(280px, 100%); border: 1px solid var(--border-2); border-radius: 8px; padding: 7px 10px;
  font: inherit; font-size: 14px; color: var(--text-main); background: #fff; }
.bulk-bar select:focus { outline: none; border-color: var(--navy-soft); box-shadow: 0 0 0 3px rgba(29, 79, 134, .12); }
.bulk-bar .help { margin: 0; flex: 1 1 220px; min-width: 0; }
.col-check { width: 34px; text-align: center; }
/* In a listing the tick column takes as little as it can: the row is already full (rc000445). */
.list-table .col-check { width: 28px; padding-left: 10px; padding-right: 0; }

/* ---------- Statuses ---------- */

.status {
  display: inline-block;
  font-size: 12.5px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 999px;
  white-space: nowrap;
}

.status-new { background: var(--success-bg); color: var(--success); }
.status-in_progress { background: var(--tint-blue); color: var(--navy-soft); }
.status-quoted { background: #f1ecfd; color: #5b3fbe; }
.status-closed { background: #eef1f5; color: var(--text-soft); }
.status-discarded { background: var(--error-bg); color: var(--error); }

/* Quote states. Separate names from the request's on purpose: a «draft» quote and a «new»
   request are not the same thing and must not share a colour by accident. */
.status-draft { background: #eef1f5; color: var(--text-soft); }
.status-sent { background: var(--tint-blue); color: var(--navy-soft); }
.status-accepted { background: var(--success-bg); color: var(--success); }
.status-rejected { background: var(--error-bg); color: var(--error); }
.status-expired { background: #fdf0e3; color: var(--warn-dark); }

/* Customer statuses (B2B). Pending in amber —there is something to do—, approved in green,
   suspended in muted red (it is not an error, it is a deliberate cut-off). */
.status-pending { background: #fdf0e3; color: var(--warn-dark); }
.status-approved { background: var(--success-bg); color: var(--success); }
.status-suspended { background: var(--error-bg); color: var(--error); }

/* ---------- Forms ---------- */

.form-block .form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px 18px;
}

.full-width { grid-column: 1 / -1; }

.field { margin-bottom: 12px; min-width: 0; }

/* 🔴 `:not(.role-checkbox)` IS NOT DECORATION. The role pills are `<label>` too, so this rule fell
   on them: it gave them `display:block`, bold and a `margin-bottom` that knocked the checkbox out of
   place against the pill's edge. It looked as if the checkbox was spilling out.

   It does not fail anywhere and is hard to attribute: the pill has its own rules further down, wins
   on `display`, and what slips through is only what it does not redefine. */
.field label:not(.role-checkbox) {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-main);
  margin-bottom: 5px;
}
/* A heading over a group of choices in a field (the password mode of the new-account form, rc000446): the
   same look as a field label, but it labels the group, not one input. */
.field .field-label {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-main);
  margin-bottom: 5px;
}

/* 🔴 IT EXCLUDES, IT DOES NOT ENUMERATE, AND THAT IS THE DIFFERENCE.
   This used to be a list of allowed types —text, search, email, password, date— and any such list
   falls short: `type="tel"` in the record's phone came out WITH NO STYLE AT ALL, like a nineties
   browser field among fields with their border and background. And it had been happening with
   `type="number"` in «Aviso de ráfaga» from before.

   It does not fail anywhere: the field works, saves and validates. It only looks bad, and only on
   that screen, so nobody discovers it until they look at it.

   The other way round —everything gets the style except what has its own shape— a new `type` fits in
   on its own. What is excluded is what is NOT a text box: checkboxes, files, colours and buttons. */
.field input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="color"]):not([type="range"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not([type="hidden"]),
.field textarea,
.field select {
  width: 100%;
  border: 1px solid var(--border-2);
  border-radius: 8px;
  padding: 8px 11px;
  font: inherit;
  font-size: 14.5px;
  color: var(--text-main);
  background: #fff;
}

.field input:focus, .field textarea:focus, .field select:focus {
  outline: none;
  border-color: var(--navy-soft);
  box-shadow: 0 0 0 3px rgba(29, 79, 134, .12);
}

/* A field shown for reading only (e.g. the assigned commercial without `salesreps:assign`, rc000451):
   it looks like a field but is plainly not editable. */
.field input:disabled, .field textarea:disabled, .field select:disabled {
  background: var(--surface-2);
  color: var(--text-soft);
  cursor: not-allowed;
}

.field textarea { resize: vertical; }
.help { margin: 5px 0 0; font-size: 12.5px; color: var(--text-faint); line-height: 1.45; }
.narrow { max-width: 460px; }

/* The 2FA components (.two-step, .totp-*, .backup-codes) live in chrome.css, shared with the
   customer account, so they do not diverge between the two sheets. */

.form-actions { display: flex; align-items: center; gap: 12px; margin-top: 16px; }

/* ORDER action bar (Phase 4d): the action that ADVANCES the order (confirm/deliver/return) on the
   left; the destructive one (cancel) pushed to the right and in red, so it does not get the same
   weight nor sit right next to the primary one. */
.order-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.order-actions-main { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.order-actions form { margin: 0; }
.order-actions-danger { margin-left: auto; }

/* Returns (RMA) in the order detail: one card per return with its lines and actions. */
.return-list { display: flex; flex-direction: column; gap: 12px; }
.return-card { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; background: var(--surface-2); }
.return-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.return-lines { margin: 8px 0 10px; padding-left: 18px; color: var(--text-soft); font-size: 14px; }
/* In a modal, the buttons are separated from the content by a rule: if the last row is a loose
   checkbox, stuck to the buttons it reads badly (operator feedback). */
.modal .form-actions { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--border); }

/* Total stock in the product detail: the big number + its red warning if it goes negative. */
.stock-total { font-size: 22px; font-weight: 600; color: var(--ink); margin: 2px 0 12px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.stock-total.stock-neg { color: var(--error, #c62828); }

/* Settings with a SIDE SELECTOR: a rail of sections on the left and the content on the right (one
   section visible at a time, panel.js switches it). */
.sc-layout { display: grid; grid-template-columns: 210px 1fr; gap: 24px; align-items: start; }
.sc-menu { display: flex; flex-direction: column; gap: 2px; border-right: 1px solid var(--border); padding-right: 12px; }
.sc-item {
  text-align: left;
  border: none;
  background: none;
  padding: 9px 12px;
  border-radius: 8px;
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--text-soft);
  cursor: pointer;
}
.sc-item:hover { background: var(--surface-2); color: var(--text-main); }
.sc-item.on { background: var(--surface-2); color: var(--ink); }
.sc-content [data-sec-panel][hidden] { display: none; }
@media (max-width: 720px) {
  .sc-layout { grid-template-columns: 1fr; }
  .sc-menu { border-right: none; flex-direction: row; flex-wrap: wrap; padding-right: 0; }
}

/* Breathing room when a filter bar sits right under a help line (e.g. the discounts page): `.help` has no
   bottom margin, so without this the text and the chips read glued together. */
.help + .filters-admin { margin-top: 14px; }


/* ---------- Buttons ---------- */

.btn-primary {
  border: 0;
  border-radius: 8px;
  background: var(--navy);
  color: #fff;
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  padding: 9px 18px;
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
}

.btn-primary:hover { background: var(--navy-light); }
.btn-primary:disabled { background: #b9c2ce; cursor: not-allowed; }

.btn-secondary {
  border: 1px solid var(--border-2);
  border-radius: 8px;
  background: #fff;
  color: var(--ink);
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  padding: 8px 16px;
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
}

.btn-secondary:hover { border-color: var(--navy-soft); }

/* AI-AGENT: base `.btn-mini` + `.btn-mini:hover` + `a.btn-mini` moved to chrome.css (rc000119) so
   the public viewer toolbar is styled. The CONTEXTUAL variants below stay here (panel-only). */

/* A button that happens to be an <a> is still a button: no underline, and the same box.
   Otherwise «Cambiar mi contraseña» and «Subir una» come out underlined like plain links. */
a.btn-primary, a.btn-secondary {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
.btn-mini-danger:hover { border-color: var(--error); color: var(--error); }

.btn-danger {
  border: 1px solid #f0bcbc;
  background: #fff;
  color: var(--error);
  border-radius: 8px;
  padding: 8px 16px;
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  cursor: pointer;
}

.btn-danger:hover { background: var(--error-bg); }
.btn-block { width: 100%; text-align: center; }

/* ---------- Alerts ---------- */

.alert {
  border-radius: 9px;
  padding: 11px 15px;
  font-size: 14.5px;
  margin-bottom: 18px;
  border: 1px solid transparent;
}

.alert-ok { background: var(--success-bg); border-color: #bfe6d0; color: #17603a; }
.alert-error { background: var(--error-bg); border-color: #f2c9c9; color: #96201f; }
.alert-warning { background: var(--warning-bg); border-color: #f3ddb4; color: #8a5200; }
.alert-success { background: var(--success-bg); border-color: #b9e6cd; color: #146c3f; }

/* An alert's button, separated from the text so it does not look like part of the sentence. */
.alert-actions { margin: 10px 0 0; }

/* A quarantined row in the list: the left bar is seen at a glance scrolling down the page, which
   is how you look at a list of fifty. */
.row-quarantined { background: var(--warning-bg); box-shadow: inset 3px 0 0 #d99b1c; }

.tag-quarantined {
  display: inline-block;
  margin-top: 3px;
  padding: 1px 7px;
  border-radius: 999px;
  background: #d99b1c;
  color: #fff;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* A textarea where HTML is written: fixed-width type and no spellcheck, because the spellchecker
   underlines every tag in red. */
.code-area {
  width: 100%;
  font-family: var(--mono);
  font-size: 13.5px;
  line-height: 1.55;
  border: 1px solid var(--border-2);
  border-radius: 8px;
  padding: 12px;
  background: var(--surface-2);
  color: var(--text-main);
  resize: vertical;
}
.code-area:focus { outline: none; border-color: var(--navy-soft); background: #fff; }

/* ---------- Metrics ---------- */

.metric-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
  gap: 14px;
  margin-bottom: 20px;
}

.metric-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 16px 16px;
}

.metric-name  { font-size: 12.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-soft); font-weight: 700; }
.metric-value { font-size: 26px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.metric-delta { font-size: 13.5px; color: var(--text-soft); font-variant-numeric: tabular-nums; }
.metric-help  { font-size: 12.5px; color: var(--text-faint); line-height: 1.4; margin-top: 4px; }
.metric-big   { font-size: 30px; font-weight: 700; color: var(--ink); margin: 0; }

/* Green and red, but always with the sign in front: some people cannot tell the two colours apart
   and a coloured arrow is not data. */
.delta-up   { color: var(--success); }
.delta-down { color: var(--error); }

/* The chart, with divs. A 300 KB library to draw thirty rectangles is paid for by whoever opens
   the page. */
/* The chart is an SVG: the geometry goes in attributes, not in `style`, because the panel's CSP
   (`style-src 'self'`) discards inline styles and the bars all came out at zero. */
.chart {
  width: 100%;
  height: 150px;
  display: block;
  overflow: visible;
}

/* The area under the line: gradient from the house colour to transparent. What gives it volume.
   🔴 `--navy` AND NOT `--navy-soft`: the brand sheet (`/brand.css`) assigns `--navy-soft` the ACCENT
   colour, not a tone of the main one — the names stuck from when everything was blue. With
   `--navy-soft` the area came out in the accent colour and fought with the notches, which are that
   same colour: it read like a chart of two things when it only draws one. */
.chart-area { fill: url(#chart-fill); }
.chart-stop-top { stop-color: var(--navy); stop-opacity: .22; }
.chart-stop-bottom { stop-color: var(--navy); stop-opacity: 0; }

.chart-line {
  fill: none;
  stroke: var(--navy);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* The notch for «this day there was a request», at the foot. Discreet on purpose: the line is the
   protagonist, and a mark per datum competing with it turns the chart into a grille. */
.chart-mark { fill: var(--accent, #bd3bff); opacity: .55; }

/* An invisible band per datum, just so the `<title>` shows on hovering over any point of that
   column and you do not have to hit right on top of the line. */
.chart-hit { fill: transparent; }
.chart-hit:hover { fill: var(--navy); opacity: .06; }

.chart-axis {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--text-faint);
  padding-top: 5px;
}

/* ---------- Settings tabs ---------- */

.tabs {
  display: flex;
  gap: 2px;
  flex-wrap: wrap;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--border);
}

.tab {
  border: 0;
  background: none;
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--text-soft);
  padding: 10px 16px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  border-radius: 8px 8px 0 0;
}

/* The period bar, with the download button at the other end. */
.tabs-bar { justify-content: space-between; align-items: center; padding-bottom: 6px; margin-bottom: 18px; }
.tabs-left { display: flex; gap: 2px; flex-wrap: wrap; }
.btn-mini-wide { font-size: 13.5px; padding: 7px 14px; margin-bottom: 4px; }

.tab:hover { color: var(--ink); background: var(--surface-2); }
.tab.is-active { color: var(--ink); border-bottom-color: var(--accent); background: none; }

/* 🔴 Without JavaScript ALL of them show one after another, which is how this screen was before
   the tabs and still works. The hiding class is added by the JavaScript; the CSS on its own hides
   nothing. */
.tab-panel.is-hidden { display: none; }
[data-settings-tabs] { display: none; }
[data-settings-tabs].is-ready { display: flex; }

/* ---------- Brand ---------- */

.brand-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
  margin-bottom: 8px;
}

.brand-slot > label { display: block; font-size: 13.5px; font-weight: 600; margin-bottom: 7px; }
.brand-slot input[type="file"] { width: 100%; font-size: 13.5px; }

/* The preview is painted over the REAL background the logo will go on. A white logo on white is
   invisible, and that is exactly the mistake you have to be able to catch at a glance here. */
.brand-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 92px;
  padding: 12px;
  margin-bottom: 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.brand-preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
.brand-preview-light { background: #fff; }
.brand-preview-dark  { background: var(--navy); }
.brand-preview-icon img { max-height: 48px; }

.color-row { display: flex; gap: 8px; align-items: center; }
.color-row input[type="color"] {
  width: 44px;
  height: 36px;
  padding: 2px;
  border: 1px solid var(--border-2);
  border-radius: 8px;
  background: none;
  cursor: pointer;
  flex: none;
}
.color-row input[type="text"] { font-family: var(--mono); text-transform: lowercase; }

/* A filter's checkbox, aligned with the fields next to it. */
.filter-check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  color: var(--text-soft);
  white-space: nowrap;
}
.filter-check input { margin: 0; }

.alert-password {
  background: #eef4fd;
  border-color: #c6dbf5;
  color: var(--ink);
}

.alert-password p { margin: 0 0 6px; }
.alert-password p:last-child { margin: 0; font-size: 13.5px; color: var(--text-soft); }

.password-code {
  font-family: var(--mono);
  font-size: 16px;
  background: #fff;
  border: 1px solid var(--border-2);
  border-radius: 6px;
  padding: 3px 9px;
  user-select: all;
}

/* ---------- Odds and ends ---------- */

/* The restore instructions: commands to copy, so they wrap nowhere. */
.code-block {
  margin: 6px 0 12px;
  padding: 12px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-main);
  overflow-x: auto;
}

/* Used by the little forms that sit inside a table cell (publish, reset password, delete). */
.inline-form { display: inline; }
/* «Attach an invoice» on the order page (rc000410): the number, the file and the button on one line,
   wrapping on a narrow screen. */
.invoice-attach { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px 14px; margin-top: 16px; }
.invoice-attach .field { margin: 0; flex: 0 1 280px; min-width: 0; }
.invoice-attach .field:first-of-type { flex-basis: 200px; }
.invoice-attach + .help { margin-top: 8px; }
/* A help line that warns: the action will not do what one would expect (rc000413). */
.help-warn { color: var(--warn-dark); font-weight: 600; }
/* The two actions under an order's status links (rc000416): side by side, the destructive one small. */
.order-access .help + .table-wrap, .order-access .help + .empty-admin { margin-top: 12px; }
.access-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 14px; }

.two-columns { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 20px; align-items: start; }

.def-list > div { display: flex; justify-content: space-between; gap: 14px; padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 14.5px; }
.def-list > div:last-child { border-bottom: 0; }
.def-list dt { margin: 0; color: var(--text-soft); }
.def-list dd { margin: 0; text-align: right; }


.error-list { margin: 0; padding-left: 20px; color: var(--error); font-size: 14px; }

/* The paginator comes from the shared partial, so it wears the same class as the public one.
   It used to have its own `.paginator-admin`, which stopped matching anything the day the
   three copies were merged into `common/`: the panel listings paginated unstyled. */
.paginator {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 16px;
  color: var(--text-soft);
  font-size: 14px;
}

.paginator a { text-decoration: none; font-weight: 600; }

.paginator-size { margin-left: auto; display: flex; align-items: center; gap: 7px; font-size: 13px; }
.paginator-size a { color: var(--text-soft); font-weight: 500; padding: 2px 6px; border-radius: 5px; }
.paginator-size a:hover { background: var(--surface-2); color: var(--navy); }
.paginator-size a.current { background: var(--navy); color: #fff; }

code { font-family: var(--mono); font-size: 13px; background: var(--surface-2); padding: 1px 6px; border-radius: 5px; }


/* ---------- Notification bell ---------- */

.notif { position: relative; }

.notif-btn {
  position: relative;
  display: flex;
  align-items: center;
  padding: 8px;
  border: none;
  background: none;
  border-radius: 8px;
  color: #9aa4b2;
  cursor: pointer;
  transition: background .15s, color .15s;
}

.notif-btn:hover { background: rgba(255, 255, 255, .09); color: #fff; }

.notif-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--error);
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

.notif.open .dd-menu { display: block; }

.notif-menu { min-width: 320px; padding: 0; }

.notif-head {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: space-between;
  padding: 11px 14px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}

/* The mute control lives in the bell's header. `margin-left:auto` sends it to the far right even
   when there is no «sin ver» counter beside it. */
.notif-mute {
  margin-left: auto;
  padding: 2px 6px;
  border: 1px solid transparent;
  border-radius: 7px;
  background: none;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.notif-mute:hover { background: var(--surface-2); border-color: var(--border); }
.notif-mute.is-muted { opacity: .55; }

.notif-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  color: var(--text-main);
  transition: background .12s;
}

.notif-item:last-of-type { border-bottom: 0; }
.notif-item:hover { background: var(--surface-2); }

/* The unseen ones carry a mark on the left: a colour alone does not survive a printout, a
   colour-blind pair of eyes or a bad screen. */
.notif-unread { box-shadow: inset 3px 0 0 var(--navy-soft); background: #f7fafd; }

.notif-title { font-size: 14px; font-weight: 600; color: var(--ink); }
.notif-body { font-size: 13px; color: var(--text-soft); }
.notif-when { font-size: 11.5px; color: var(--text-faint); }

.notif-empty { margin: 0; padding: 22px 14px; text-align: center; font-size: 13.5px; color: var(--text-soft); }

.notif-all {
  display: block;
  padding: 9px 14px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--navy-soft);
  text-decoration: none;
}

.notif-all:hover { background: var(--surface-2); }

/* ---------- Modals ---------- */

/* The panel had none: confirmations were a window.confirm, which the browser paints its own way
   and which cannot be read on a phone. Same shape as the public catalogue's. */

/* 🔴 On a phone, `inset: 0` measures the LARGE viewport —the one there would be with no address bar
   or controls at the bottom— so the modal spills off both ends and what is lost is the title on top
   and the confirm button at the bottom. `height: 100dvh` measures what is visible NOW, and it is its
   own fallback: whoever does not understand it discards the line and keeps the usual `inset: 0`.
   The same fix is in the public site, with the long detail. */
.modal {
  position: fixed;
  inset: 0;
  height: 100dvh;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding:
    max(24px, env(safe-area-inset-top))
    max(24px, env(safe-area-inset-right))
    max(24px, env(safe-area-inset-bottom))
    max(24px, env(safe-area-inset-left));
}

.modal[hidden] { display: none; }

.modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(10, 37, 64, .45);
}

.modal-box {
  position: relative;
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: 0 24px 60px rgba(10, 37, 64, .3);
  padding: 24px;
  width: min(520px, 100%);
  max-height: 90dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.modal-narrow { width: min(420px, 100%); }

/* An account record: name, two surnames, phone, email, role, centre, status, notices and roles. In
   the 520px of `.modal-box` that comes out as a very long column you have to scroll through in full,
   and the window looks small with the empty screen around it. Here it widens and the fields go two
   at a time, which is how you read a record. */
.modal-profile {
  width: min(820px, 100%);
  max-height: 92dvh;
}

.modal-profile .form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.modal-profile .form-grid .full-width { grid-column: 1 / -1; }

/* On mobile two columns leave the fields unusable: a single one, full width. */
@media (max-width: 640px) {
  .modal-profile .form-grid { grid-template-columns: minmax(0, 1fr); }
}

.modal-close {
  position: absolute;
  top: 10px;
  right: 12px;
  border: 0;
  background: none;
  font-size: 22px;
  line-height: 1;
  color: var(--text-soft);
  cursor: pointer;
}

.modal-close:hover { color: var(--ink); }
.modal-box h2 { margin: 0 0 16px; font-size: 17px; color: var(--ink); }

.confirm-title { margin: 0 0 10px; }
.confirm-text { margin: 0 0 20px; color: var(--text-main); font-size: 15px; line-height: 1.5; }
.confirm-actions { display: flex; gap: 10px; justify-content: flex-end; }

/* ---------- Document viewer ---------- */

/* AI-AGENT: `.modal-wide` + the `.viewer-*` component moved to chrome.css (rc000119 review) so the
   customer's order/quote viewer, on public.css pages, gets the same styles. Do not re-add them here. */

/* ---------- Login ---------- */

.page-login { background: var(--dark); }

.login-page {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 30px 20px;
}

.login-box {
  background: var(--surface);
  border-radius: 14px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, .35);
  padding: 30px 30px 32px;
  width: min(370px, 100%);
}

/* The install form asks for six things, not two: at the login width it becomes a very long column
   you have to scroll through in full to see whether something is missing. */
.login-box-wide { width: min(560px, 100%); }
.install-intro {
  margin: -8px 0 20px;
  font-size: 14px;
  color: var(--text-soft);
  line-height: 1.55;
}
/* 🔴 The panel's general `.help` carries a NEGATIVE top margin (`-8px`), because there it sits right
   under a field that already brings its own gap. Not here: this box's `input` has no bottom margin,
   so that -8px put the help line OVER the field's border. It is written in full instead of tweaking
   the negative, so it does not depend on what the other one is worth. */
.login-box-wide .help {
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 1.45;
}

.login-brand { display: flex; flex-direction: column; align-items: center; gap: 5px; margin-bottom: 22px; color: var(--ink); }
.login-brand span { font-size: 13.5px; color: var(--text-soft); }

.login-box label {
  display: block;
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-soft);
  font-weight: 700;
  margin: 14px 0 5px;
}

.login-box input {
  width: 100%;
  border: 1px solid var(--border-2);
  border-radius: 8px;
  padding: 10px 12px;
  font: inherit;
  font-size: 15px;
  background: #fff;
  color: var(--text-main);
}

.login-box input:focus { outline: none; border-color: var(--navy-soft); box-shadow: 0 0 0 3px rgba(29, 79, 134, .14); }

/* With help texts in between, the next label needs more air so it is clear which field each thing
   belongs to. */
.login-box-wide .help + label { margin-top: 18px; }
.login-box .btn-primary { margin-top: 22px; }

/* The country <select> matches the text inputs (the taller registration form). */
.login-box select {
  width: 100%;
  border: 1px solid var(--border-2);
  border-radius: 8px;
  padding: 10px 12px;
  font: inherit;
  font-size: 15px;
  background: #fff;
  color: var(--text-main);
}
.login-box select:focus { outline: none; border-color: var(--navy-soft); box-shadow: 0 0 0 3px rgba(29, 79, 134, .14); }

/* The registration form is WIDE and laid out in a 2-column grid, so the fiscal + billing + access
   fields read as a compact card instead of one very tall column. */
.login-box.login-box-form { width: min(680px, 100%); }

/* Section divider: a small caption with a hairline above, marking each block. */
.form-section {
  margin: 22px 0 8px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--navy-soft);
}
.form-section:first-of-type { border-top: 0; padding-top: 0; margin-top: 6px; }

/* Two columns; a `.span2` field takes the full width. Fields carry their own label/input styling
   (.field in panel.css), so the register form reuses it and needs no bespoke input rules. */
.reg-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 16px; }
.reg-grid .field { margin: 0 0 10px; }
.reg-grid .span2 { grid-column: 1 / -1; }
@media (max-width: 520px) { .reg-grid { grid-template-columns: 1fr; } }

/* ---- Registration, design A (rc000392): a FLAT full-page split — a panel on the left, the form on the right.
   Not a card: the operator rejected the floating box on the dark page. The panel is as tall as the screen and
   stays in view (sticky) while the form scrolls, so its two ends — the benefits and «what happens next» —
   are always visible instead of sitting a long form apart. */
.page-register { background: var(--surface); }
.page-register .login-page { display: block; padding: 0; }
.reg-shell { display: grid; grid-template-columns: clamp(320px, 34%, 640px) minmax(0, 1fr); align-items: start; min-height: 100vh; }
.reg-shell .login-box.login-box-form {
  width: 100%; max-width: 780px; margin: 0 auto; padding: 48px 40px 40px;
  border-radius: 0; box-shadow: none; background: none;
}
.reg-side {
  position: sticky; top: 0; height: 100vh; overflow-y: auto; /* desktop only (static on phones), so vh is exact */
  display: flex; flex-direction: column; gap: 24px; padding: 48px 44px 40px; color: #fff;
  background: linear-gradient(150deg, var(--navy) 0%, var(--navy-light) 60%, var(--navy-soft) 120%);
}
/* The page footer (back to the catalogue · version) on the light page: a quiet strip under the split. */
.page-register .login-footer { margin: 0; padding: 14px 20px; border-top: 1px solid var(--border); color: var(--text-soft); }
.page-register .login-footer a { color: var(--navy-soft); }
.reg-side-brand { display: inline-block; }
.reg-side-logo { display: block; height: 44px; width: auto; max-width: 160px; object-fit: contain; object-position: left center; }
.reg-side h1 { margin: 0; font-size: 26px; line-height: 1.2; color: #fff; }
.reg-side-lead { margin: 8px 0 0; font-size: 14.5px; color: #d4e4f7; max-width: 36ch; }
.reg-benefits { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.reg-benefits li { display: flex; align-items: flex-start; gap: 12px; font-size: 14px; }
.reg-benefits svg { flex: none; width: 26px; height: 26px; color: #9ec7f5; }
.reg-benefits b { display: block; }
.reg-benefits small { font-size: 13px; color: #c6d8ee; }
.reg-next { margin-top: auto; padding-top: 18px; border-top: 1px solid rgba(255, 255, 255, .18); font-size: 13px; color: #d4e4f7; }
.reg-next b { display: block; margin-bottom: 8px; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: #fff; }
.reg-next ol { margin: 0; padding-left: 18px; display: grid; gap: 4px; }
.reg-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px; margin-bottom: 4px; }
.reg-head h2 { margin: 0; font-size: 21px; color: var(--ink); }
.reg-head span { margin-left: auto; font-size: 13.5px; color: var(--text-soft); }
.reg-form .alert { margin-top: 14px; }
/* The form's own rhythm (the mockup): sentence-case labels, section captions without hairlines, one gap. */
.reg-form .form-section, .reg-form .form-section:first-of-type { margin: 22px 0 10px; padding-top: 0; border-top: 0; }
.reg-form .reg-grid { gap: 12px 16px; }
.reg-form .reg-grid .field { margin: 0; }
.reg-form .field label { margin: 0 0 5px; font-size: 13px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--ink); }
.reg-form .field label .muted { font-weight: 500; }
.reg-form .btn-primary { margin-top: 24px; padding: 12px 20px; font-size: 15px; }

/* Account type: two cards. They are <label>s inside .login-box, so the label / input rules of the box
   (uppercase block labels, full-width inputs) are overridden here by the extra class. */
.acct-cards { margin: 0; padding: 0; border: 0; min-width: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.acct-cards legend { grid-column: 1 / -1; width: 100%; padding: 0; }
.login-box label.acct-card {
  position: relative; display: flex; align-items: center; gap: 12px; margin: 0; padding: 12px 14px; cursor: pointer;
  border: 1.5px solid var(--border-2); border-radius: 12px; background: #fff;
  font-size: 14px; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--text-main);
}
.login-box .acct-card input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer; }
.acct-card svg { flex: none; width: 30px; height: 30px; color: var(--navy-soft); }
.acct-card b { display: block; font-size: 14.5px; color: var(--ink); }
.acct-card small { font-size: 12.5px; color: var(--text-soft); }
.login-box label.acct-card:has(input:checked) { border-color: var(--navy-soft); box-shadow: 0 0 0 3px rgba(29, 79, 134, .16); }
.login-box label.acct-card:has(input:focus-visible) { outline: 2px solid var(--navy-soft); outline-offset: 2px; }
/* The other account type's block goes away with the choice — no JavaScript needed. */
.reg-form:has(input[name="account_type"][value="individual"]:checked) [data-when="company"],
.reg-form:has(input[name="account_type"][value="company"]:checked) [data-when="individual"] { display: none; }

/* Password show / hide. */
.pw-wrap { position: relative; }
.login-box .pw-wrap input { padding-right: 84px; }
.pw-toggle { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); padding: 6px 8px; border: 0; background: none;
  color: var(--navy-soft); font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer; }
.pw-toggle[hidden] { display: none; }

@media (max-width: 860px) {
  .reg-shell { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  /* On a phone the panel is just its heading: the benefits and the steps would push the form down. */
  .reg-side { position: static; height: auto; overflow: visible; gap: 12px; padding: 22px 20px; }
  .reg-side h1 { font-size: 21px; }
  .reg-benefits, .reg-next { display: none; }
  .reg-shell .login-box.login-box-form { padding: 22px 18px 24px; }
}
@media (max-width: 520px) { .acct-cards { grid-template-columns: minmax(0, 1fr); } }

/* Address autocomplete (IGN CartoCiudad, ES only) — a suggestion box under the address field. Same look
   as the public quote form (whose copy lives in public.css); the registration form is on panel.css. */
.address-picker { position: relative; }
.address-suggest {
  position: absolute;
  z-index: 30;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  max-height: 240px;
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--border-2);
  border-radius: 8px;
  box-shadow: 0 10px 24px rgba(10, 37, 64, .16);
  padding: 4px;
}
.address-choice {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  padding: 8px 10px;
  border-radius: 6px;
  font: inherit;
  font-size: 14px;
  color: var(--text-main);
  cursor: pointer;
}
.address-choice:hover { background: var(--surface-2); color: var(--ink); }

/* The «¿ya tienes cuenta? Entra» / «¿aún no? Regístrate» under the customer form. Centred and
   discreet, with air above. */
.login-alt {
  margin: 16px 0 0;
  text-align: center;
  font-size: 14px;
  color: var(--text-soft);
}

/* 🔴 IT WAS A SINGLE ROW WITH NO WRAPPING, AND ON A PHONE IT DOES NOT FIT.
   Link + copyright + version measure quite a bit more than 360 px. In `display:flex` without
   `flex-wrap` the children do NOT drop to a new line: they compress until they break the words or
   spill off the side, and then the horizontal scrollbar appears and the whole screen drags. It shows
   only on mobile, which is where you do not look when laying out.

   🔑 By wrapping, each piece falls where it fits and stays centred. The separator dots are hidden
   below 480 px: a `·` at the start of a new line separates nothing, it just looks like a typo. */
.login-footer {
  color: #8b97a7;
  font-size: 13px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* So it does not stick to the edge on a narrow screen, nor to the phone's gesture bar. */
  padding: 0 12px max(0px, env(safe-area-inset-bottom));
  max-width: 100%;
}
.login-footer a { color: #b9c3cf; text-decoration: none; }

@media (max-width: 480px) {
  /* In two lines it reads better than in four loose pieces: the link on top, the signature below. */
  .login-footer .sep { display: none; }
  .login-footer { gap: 2px 10px; line-height: 1.6; }
  .login-footer a { flex-basis: 100%; margin-bottom: 2px; }
}

/* ---------- Footer ---------- */

/* Same reason as in the login footer: a flex row with no wrapping does not drop to a new line, it
   crushes the text and breaks the words into vertical columns. Here the test caught it, not the eye. */
.footer-admin {
  padding: 14px 22px;
  border-top: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-faint);
  font-size: 13px;
  display: flex;
  flex-wrap: wrap;
  text-align: center;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.sep { color: var(--border-2); }
.version { font-family: var(--mono); }
.heart { color: #e0245e; }

@media (max-width: 980px) {
  .two-columns { grid-template-columns: minmax(0, 1fr); }
  /* The responsive topbar now lives in chrome.css (shared). */
}

/* ---------- Accounts and roles ---------- */

/* The account name opens its record. It is a <button> and not an <a> because it does not lead to
   any address: without `href` a link cannot be activated with the keyboard nor announced by the
   screen reader. Here its button look is removed, not its behaviour. */
.btn-link {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--navy, #1b3c6d);
  text-align: left;
  cursor: pointer;
}

/* No underline on hover: in a table, underlining the hovered cell makes the row «jump» and it reads
   as if the text changed. The tone change already says it can be clicked, and it is the same
   criterion as the rest of the panel's links (see `.more-link`). */
.btn-link:hover { color: var(--accent); }
.btn-link:focus-visible { outline: 2px solid var(--navy-soft); outline-offset: 2px; border-radius: 3px; }

.account-roles { display: flex; gap: 8px; flex-wrap: wrap; min-width: 0; }

.role-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border-2);
  border-radius: 999px;
  padding: 5px 12px;
  font-size: 13.5px;
  color: var(--text-soft);
  cursor: pointer;
  user-select: none;
  background: #fff;
}

.role-checkbox:hover { border-color: var(--navy-soft); color: var(--ink); }
.role-checkbox:has(input:checked) { background: #eef4fd; border-color: #b9d3f0; color: var(--ink); font-weight: 600; }
.role-checkbox input { margin: 0; }

/* The actions that are not «guardar» (new password, delete) go at the foot of the record,
   separated by a line: each is its own <form>, because nesting them inside the save one is not
   valid HTML and the browser skips them without saying anything. */
.account-extra {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--border-2);
}

/* In the accounts table several tags fit per cell (active + must change password, yes + blind
   copy). Without this they stick together and on mobile they spill out of the column. */
.table td .status { margin: 1px 4px 1px 0; }

.panel-card-head code { font-size: 13px; color: var(--text-soft); }


/* ---------- Message template editor ----------
   Form on the left, live preview on the right. It collapses to one column below 1100 px: two
   columns on a laptop screen leave a textarea too narrow to read a line of HTML in. */

.editor-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  min-height: 560px;
}

.editor-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  min-width: 0;
}

.editor-form .field { margin: 0; }
.editor-grow { display: flex; flex-direction: column; flex: 1; }
.editor-grow textarea { flex: 1; min-height: 260px; }

/* Monospace, because what goes in here is markup: proportional type hides a missing angle
   bracket among the letters. */
.editor-code {
  font-family: ui-monospace, "SF Mono", "Cascadia Code", "Fira Code", Menlo, Consolas, monospace;
  font-size: 13.5px;
  line-height: 1.55;
  resize: vertical;
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
}

.chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.chips-label { font-size: 12.5px; color: var(--text-soft); margin-right: 2px; }

/* `.chip` (base + hover + active) lives in chrome.css — the shared chrome owns it and loads after this
   sheet, so its rules win. Keeping a copy here only led to the active chip showing a border but no fill
   (chrome's later `.chip` overrode this sheet's `.chip-on` background). Add a `.chip { cursor: pointer;
   border: 1px solid var(--border-2) }` here only if a panel-only chip ever needs it. */

/* A destructive action-link (delete a segment): in muted red, like the other dangers. */
.btn-link-danger { color: var(--error); }
.btn-link-danger:hover { color: #96201f; }

/* Honeypot for the customer registration form. Out of view without using display:none (which some
   bots read to skip it). Same rule as in public.css, here because the login pages load the panel
   sheet. */
.trap {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.editor-preview {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--surface-2);
}

.editor-tabs {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 8px 12px 0;
  border-bottom: 1px solid var(--border);
}

.editor-tab {
  border: 0;
  background: none;
  padding: 7px 12px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-soft);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.editor-tab.is-active { color: var(--navy); border-bottom-color: var(--navy); }
.editor-status { margin-left: auto; font-size: 12.5px; }

.preview-frame { flex: 1; width: 100%; border: 0; background: #fff; min-height: 480px; }

.preview-source {
  flex: 1;
  margin: 0;
  padding: 14px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.5;
  background: #0f1b2b;
  color: #d7e3f4;
  min-height: 480px;
}

@media (max-width: 1100px) {
  .editor-split { grid-template-columns: minmax(0, 1fr); }
  .editor-form { border-right: 0; border-bottom: 1px solid var(--border); }
  .preview-frame, .preview-source { min-height: 380px; }
}

/* ---------- Message templates ---------- */

/* The 🌐 language picker + Reset in each message's header (Fase email-i18n). ONE globe opens a dropdown of
   the available languages (reuses `.dd-menu`/`.dd-item` + the `.open` toggle from the user menu). */
.tpl-head-actions { display: flex; gap: 8px; align-items: center; margin-left: auto; }
.tpl-lang-dd { position: relative; }
.tpl-lang-dd .dd-menu { right: auto; left: 0; min-width: 160px; }
.tpl-lang-dd.open .dd-menu { display: block; }
.dd-item.dd-current { font-weight: 600; background: var(--surface-2, #f1f5f9); }

.library-btn {
  font: inherit;
  font-size: 13.5px;
  padding: 6px 12px;
  border: 1px solid var(--border-2);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-main);
  cursor: pointer;
  white-space: nowrap;
}

.library-btn:hover { border-color: var(--navy); }

/* The one in use reads as in use at a glance: which message the customer gets is the whole
   point of this screen. */
.library-btn.is-active {
  background: var(--navy);
  border-color: var(--navy);
  color: #fff;
  font-weight: 600;
}

/* Full-screen editing. Writing HTML in half a column inside a long page is what «the editor is
   a bit small» meant. */
/* True full screen: on mobile `inset: 0` goes under the browser controls, and the editor's bottom
   edge ends up out of the finger's reach. */
.editor-pane.is-expanded {
  position: fixed;
  inset: 0;
  height: 100dvh;
  z-index: 400;
  background: var(--bg);
  padding: 16px;
  overflow: auto;
}

.editor-pane.is-expanded .editor-split { min-height: calc(100dvh - 32px); }
.editor-pane.is-expanded .preview-frame,
.editor-pane.is-expanded .preview-source { min-height: calc(100vh - 120px); }

.editor-expand { margin-left: auto; }
.editor-tabs .editor-status { margin-left: 12px; }

/* The filter button, once the filters apply on their own. See panel.js: hidden rather than
   removed, so Enter still submits the free-text box. The variants axis picker applies the same way
   (ticking an axis reshapes the matrix on its own — no «Apply» button with JavaScript on). */
.filters-admin.is-auto button[type="submit"],
.variants-axes-form.is-auto button[type="submit"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- Licensing ---------- */

.value-line { margin: 0; font-size: 15px; color: var(--text-main); }

.licence-text {
  margin: 0;
  padding: 18px 20px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 13.5px;
  line-height: 1.65;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--text-main);
  max-height: 60dvh;
  overflow: auto;
}

/* ---------- The footer at the bottom, always ----------
   🔴 The body was already a flex column with `min-height:100vh`, but nothing inside it grew: with
   a short page —an error, the cookie policy, an empty search— the footer sat wherever the text
   ended, halfway up a screen full of background. Whatever the page's main block is has to take
   the leftover height.

   `100dvh` for phones, where `100vh` counts a browser bar that is not there and leaves the
   footer just below the fold; the `100vh` line stays in front of it for anything that does not
   understand the newer unit. */

body { min-height: 100vh; min-height: 100dvh; }

body > main,
body > .page,
body > .legal,
body > .content { flex: 1 0 auto; }

body > footer { flex: none; }

/* A card that has to be read before the one under it. */
.alert-card {
  border-left: 4px solid #d97706;
  background: #fffbf3;
}

.alert-card h2 { color: var(--warn-dark); }

.alert-list { margin: 8px 0 6px; padding-left: 20px; }
.alert-list li { margin: 2px 0; }

/* ---------- Folds («Leer el documento entero», «Qué puedes insertar») ----------
   Glued to the buttons above them they read as one more button. They get air and a rule, so it
   is clear that what follows is a section and not an action. */

.placeholders {
  margin-top: 26px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.placeholders > summary {
  cursor: pointer;
  padding: 5px 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--navy);
}

.placeholders > summary:hover { color: var(--navy-soft); }
.placeholders[open] > summary { margin-bottom: 14px; }

/* A cell that must never be broken up: «PRE-2026-000001» split over three lines stops looking
   like a number. */
.table td.nowrap, .table th.nowrap { white-space: nowrap; }

/* ---------- Editing inside a table ----------
   🔴 These fields read as text until you go near them. Boxed like the ones in a form they shouted
   over the rest of the row: eight bordered rectangles in a table make the numbers the least
   visible thing on a screen whose whole job is showing numbers. The border appears on hover and
   the focus ring is the same one every other field in the panel uses. */

.cell-input, .free-name, .free-unit {
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
  font: inherit;
  font-size: 14.5px;
  color: var(--text-main);
  transition: border-color .12s ease, background-color .12s ease;
}

.cell-input {
  width: 100%;
  max-width: 96px;
  padding: 5px 8px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.cell-input:hover, .free-name:hover, .free-unit:hover {
  border-color: var(--border-2);
  background: var(--surface);
}

.cell-input:focus, .free-name:focus, .free-unit:focus {
  outline: none;
  border-color: var(--navy-soft);
  background: var(--surface);
  box-shadow: 0 0 0 3px rgba(29, 79, 134, .12);
}

.cell-input::placeholder, .free-name::placeholder, .free-unit::placeholder { color: var(--text-faint); }

.cell-remove { text-align: center; }

/* A free line is text the salesperson owns: it is edited in place, and it says so. */
.free-name { width: 100%; max-width: 320px; padding: 5px 8px; }
.free-unit { width: 74px; padding: 4px 8px; font-size: 13.5px; margin-top: 2px; }
.free-tag { font-size: 12px; margin-left: 6px; }

.new-line td { background: #fbfcfe; }
.new-line .free-name { max-width: 380px; }

/* Our own suggestion list: short, with a ceiling, and anchored to the field. The browser's own
   datalist popup is as tall as the catalogue and cannot be styled at all. */
.line-picker { position: relative; display: block; }

.line-suggest {
  position: absolute;
  z-index: 20;
  top: calc(100% + 2px);
  left: 0;
  width: min(420px, 60vw);
  max-height: 232px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: 8px;
  box-shadow: 0 10px 24px rgba(10, 37, 64, .14);
  padding: 4px;
}

.line-choice {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  padding: 7px 9px;
  border-radius: 6px;
  font: inherit;
  font-size: 13.5px;
  color: var(--text-main);
  cursor: pointer;
}

.line-choice:hover { background: var(--surface-2); color: var(--navy); }

/* A line on its way out: struck through and faded, so «quitar» is visible before saving and the
   same button brings it back. */
.is-removing { opacity: .45; }
.is-removing td { text-decoration: line-through; }
.is-removing input { text-decoration: none; }

.line-more {
  display: block;
  padding: 6px 9px;
  font-size: 12px;
  color: var(--text-faint);
  border-top: 1px solid var(--border);
  margin-top: 2px;
}

.presets { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 2px 0 10px; }

/* ---------- Collapsible help steps ----------
   The steps to register the OAuth application live ON the screen, not in a separate manual: it is
   set up once, nobody knows it by heart, and a manual that lives somewhere else is a manual that
   goes unread. Collapsed, so as not to eat the screen of whoever already has it done. */

.help-steps {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0 14px;
  margin-bottom: 12px;
  background: var(--surface-2);
}

.help-steps > summary {
  padding: 10px 0;
  cursor: pointer;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-main);
}

.help-steps ol { margin: 0 0 12px; padding-left: 20px; font-size: 14px; color: var(--text-soft); }
.help-steps li { margin-bottom: 7px; line-height: 1.5; }
.help-steps code { font-family: var(--mono); font-size: 13px; background: #fff; padding: 1px 5px; border-radius: 4px; }

/* ---------- Foot of a form inside a modal ----------
   The help text and the button crowded against the bottom edge: three paragraphs in a row with no
   air and the button stuck to the last one. A rule and some space separate «what you have to read»
   from «what you have to press». */
.modal-profile .form-actions {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

/* Consecutive help paragraphs: let them breathe between each other. */
.help + .help { margin-top: 8px; }

/* The filter bar with its main action on the right, like the Metrics one. */
.filters-bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}

.filters-bar .filters-admin { margin-bottom: 0; flex: 1 1 auto; }

/* A link inside a data record: it stands out by colour, not by underline. In a «this is what there
   is» block the underline makes it look like an editable field. */
.link-clean { text-decoration: none; }
.link-clean:hover { color: var(--accent); }

/* ---------- The page builder (Fase 3): palette + editors on the left, big live canvas on the right ----

   🔴 The canvas is the star: the old editor's preview was a narrow column ("una mini ventana"). Here the
   iframe is drawn at the real device width and scaled to fill a LARGE stage, so you lay out against what
   people will actually see. The left column is the model + save (and the no-JS fallback editor). */

.block {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px 4px;
  background: var(--surface-2);
}
.block-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.block-name { font-weight: 700; font-size: 14px; color: var(--ink); }
.block-head .muted { font-size: 13px; flex: 1 1 200px; min-width: 0; }
.block-controls { display: flex; gap: 6px; margin-left: auto; }

/* The whole builder fills the content area and manages its own scrolling (two independent columns). */
/* 🔴 The builder breaks OUT of the panel's centred `.content` box (max-width 1360 + padding) and fills the
   whole area — otherwise it sits in a narrow column with the page empty around it. `:has()` scopes this to
   the page that contains the builder; no other panel page is touched. The chain is flex all the way down so
   the two columns fill the height and scroll independently, and the canvas fills the vertical space. */
.content:has(.builder) { max-width: none; padding: 0; display: flex; flex-direction: column; }
/* The two-pane builder fills the viewport and NEVER grows the window: the left panel (.builder-side)
   scrolls on its own, so selecting a block reveals its fields without yanking the whole page down
   (rc000383). The design always intended this (the stage is overflow:hidden, «the page scrolls inside
   the iframe»); the height constraint was missing, so a tall left form grew the body past the viewport.
   Desktop only — below 1080px the builder stacks (.builder-body: block) and the window scrolls normally. */
@media (min-width: 1081px) {
  body:has(.builder) { height: 100vh; height: 100dvh; overflow: hidden; }
  .content:has(.builder) { flex: 1 1 0; min-height: 0; }
}
.builder { flex: 1; min-height: 0; display: flex; flex-direction: column; }

.builder-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 10px 16px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
  flex: none;
  z-index: 3;
}
.builder-bar-left { display: flex; align-items: center; gap: 12px; min-width: 0; }
.builder-title { font-size: 18px; margin: 0; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.builder-bar-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.builder-devices { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.builder-devices .btn-mini.is-active { background: var(--navy); color: #fff; border-color: var(--navy); }
.builder-devices .muted { margin-left: 4px; font-size: 12.5px; }

/* Custom canvas size + saved presets. */
.builder-custom { display: inline-flex; align-items: center; gap: 4px; margin-left: 4px; }
.builder-size { width: 58px; padding: 4px 6px; border: 1px solid var(--border-2); border-radius: 6px; font: inherit; font-size: 12px; }
.builder-size-x { color: var(--text-soft); font-size: 12px; }
.builder-presets { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.preset-chip { display: inline-flex; align-items: center; }
.preset-chip .btn-mini { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.preset-chip .btn-mini.is-active { background: var(--navy); color: #fff; border-color: var(--navy); }
.preset-x {
  border: 1px solid var(--border-2); border-left: 0; background: #fff; cursor: pointer;
  padding: 4px 6px; border-radius: 0 6px 6px 0; color: var(--text-soft); font: inherit; line-height: 1;
}
.preset-x:hover { color: #c0392b; }

/* Publish button in the bar: the COLOUR is the state — amber call-to-action when it is a draft, green
   when it is live (click withdraws). */
.btn-publish {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 16px; border-radius: 8px; border: 1px solid transparent;
  font: inherit; font-weight: 600; cursor: pointer; white-space: nowrap;
}
.btn-publish.is-draft { background: #f59e0b; color: #fff; }
.btn-publish.is-draft:hover { background: #d98706; }
.btn-publish.is-live { background: var(--tint-ok); color: #146c3a; border-color: #9bd6b2; }
.btn-publish.is-live:hover { background: #dcefe3; }
.btn-publish .dot { width: 8px; height: 8px; border-radius: 50%; background: #1aa251; }
.builder-danger { margin-top: 10px; }

.builder-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(300px, 360px) minmax(0, 1fr);
  gap: 16px;
  padding: 14px 16px 16px;
  align-items: stretch;
}

/* Left: palette + block editors + settings. Its own scroll so the canvas stays put. */
.builder-side {
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  padding: 14px 14px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.builder-aside-title { font-size: 14px; margin: 0 0 4px; color: var(--ink); text-transform: uppercase; letter-spacing: .04em; }
.palette-list { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.palette-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
  padding: 9px 10px;
  border: 1px solid var(--border-2);
  border-radius: 8px;
  background: #fff;
  font: inherit;
  cursor: grab;
}
.palette-item:hover { border-color: var(--accent); background: var(--surface-2); }
.palette-item:active { cursor: grabbing; }
.palette-item-name { font-weight: 600; font-size: 13px; color: var(--ink); }
.palette-item .muted { font-size: 11.5px; line-height: 1.35; }

/* Custom palette drag (native cross-frame DnD into the iframe is unreliable): a ghost follows the cursor
   and a transparent overlay over everything lets the panel keep receiving the pointer while it is over the
   canvas iframe. */
.builder-dragoverlay { position: fixed; inset: 0; height: 100dvh; z-index: 900; cursor: grabbing; }
.builder-dragghost {
  position: fixed; z-index: 901; pointer-events: none;
  transform: translate(10px, 10px);
  padding: 6px 10px; border-radius: 7px;
  background: var(--navy); color: #fff; font-size: 12px; font-weight: 600;
  box-shadow: 0 6px 18px rgba(10, 37, 64, .3);
}

.builder-blocks { display: flex; flex-direction: column; gap: 12px; }
/* The selected block's editor, lit up when you click it in the canvas. */
.block-selected { outline: 2px solid var(--accent); outline-offset: 1px; }
.builder-settings > summary { cursor: pointer; font-weight: 600; color: var(--ink); padding: 4px 0; }
.builder-form-actions { margin-top: 4px; }

/* With the builder live, the per-block ↑↓/Quitar fallback controls and the bottom Save are hidden — the
   canvas chrome and the top Save replace them. Without JS (`.is-enhanced` absent) they stay visible. */
.builder.is-enhanced [data-nojs-controls] { display: none; }
.builder.is-enhanced .builder-form-actions { display: none; }

/* Right: the big canvas stage. The iframe is positioned by JS (width + transform). */
.builder-canvas-area { min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: 8px; }
.builder-stage {
  position: relative;
  width: 100%;
  flex: 1;
  min-height: 0;
  /* hidden, not auto: the frame is drawn at the device width (wider than the stage) and shrunk with
     `transform`, which does NOT shrink its layout box — `auto` would show phantom scrollbars for that
     box. The scaled content is centred and fits; the page scrolls INSIDE the iframe. */
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 8px;
  background:
    linear-gradient(45deg, #eef1f5 25%, transparent 25%, transparent 75%, #eef1f5 75%),
    linear-gradient(45deg, #eef1f5 25%, transparent 25%, transparent 75%, #eef1f5 75%) #f7f9fb;
  background-size: 20px 20px;
  background-position: 0 0, 10px 10px;
  display: flex;
  justify-content: center;
}
.builder-frame {
  border: 0;
  display: block;
  transform-origin: top center;
  width: 1280px;
  height: 920px;
  background: #fff;
  box-shadow: 0 6px 24px rgba(10, 37, 64, .14);
}
.builder-stage-help { margin: 0; }

@media (max-width: 1080px) {
  /* Stacked on a narrow screen: palette/editors above, canvas below, and the page scrolls as usual
     instead of two fixed-height columns. */
  .builder { min-height: auto; }
  .builder-body { display: block; padding: 12px; }
  .builder-side { min-height: 0; margin-bottom: 12px; }
  .builder-stage { height: 70vh; }
}

/* ---------- The preview of a catalogue card ----------

   The height is fixed and generous: the tallest card —with an offer tag, struck-through price and
   five lines of features— fits in full. If one day it did not fit, the frame scrolls inside instead
   of clipping, which is what would make you believe the card ends where it does not. */

.modal-card { width: min(420px, 100%); }

.card-frame {
  width: 100%;
  height: 560px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  display: block;
  margin-top: 10px;
}

/* The mailbox's two actions, on the same row. «Desconectar» lives in its own form —it cannot go
   inside the other one— so they are brought together here with `form="…"` on the main button. */
.form-actions-oauth {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* The little permission window: it lasts two seconds and only says whether it went well. */
.oauth-window {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100dvh;
  margin: 0;
  padding: 22px;
  background: var(--bg);
  text-align: center;
}

.oauth-notice { max-width: 380px; }
.oauth-notice p { margin: 0 0 8px; font-size: 15px; line-height: 1.6; color: var(--text-main); }
.oauth-ok   { color: #1f7a4d; font-size: 17px; }
.oauth-fail { color: #b3261e; font-size: 17px; }

/* The button of a notice that offers something («Actualizar»). It is aligned with the text, not at
   the very top: `.toast` aligns to the start so a three-line message does not centre the cross. */
.toast .btn-mini {
  flex: none;
  align-self: center;
  white-space: nowrap;
}

/* ---------- Notices: the floating ones and the page one ----------

   🔑 THEY ARE READ AT TWO LEVELS. The headline says what happened and is read at a glance; the line
   below says what to do with it. In a single running line the second half is lost, because the eye
   stops as soon as it understands the first.

   🔴 THE BACKGROUND IS A GRADIENT OF THE STATE COLOUR, NOT A FLAT COLOUR. Over the panel surface,
   falling to the right: the colour mark stays next to the icon —where reading starts— and fades
   before the text, which thus keeps its contrast. A uniform tint would do exactly the opposite: it
   would lower the contrast of the whole message to decorate the background. */

.toasts {
  /* Right below the bar (58 px) and on the RIGHT, which is where notices are expected. Centred they
     ended up in the middle of the screen, covering the content being read. */
  position: fixed;
  top: calc(58px + 12px);
  right: max(14px, env(safe-area-inset-right));
  z-index: 500;
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: none;
  width: min(460px, calc(100vw - 24px));
}

.toast,
.page-alert {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 13px 14px;
  border-radius: 12px;
  border: 1px solid var(--border);
  font-size: 14px;
  line-height: 1.45;
}

/* 🔴 The FLOATING toast matches the storefront one (operator 2026-10-02): a glassy, translucent COLOURED
   card with white text — same look across the app. It stays top-RIGHT (centred covered the content being
   read). The inline `.page-alert` is NOT a floating card, so it keeps its light look (below). */
.toast {
  color: #fff;
  border-color: rgba(255, 255, 255, .28);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .35);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
}

/* The page alert does not float, so it carries no shadow of a suspended element: it blends into
   the content, which is the whole difference between the two. */
.page-alert {
  margin: 0 0 18px;
  background:
    linear-gradient(100deg, rgba(179, 38, 30, .16) 0%, rgba(179, 38, 30, .04) 42%, rgba(179, 38, 30, 0) 72%),
    var(--surface);
  border-color: rgba(179, 38, 30, .3);
}

.toast-ok { background: rgba(30, 150, 70, .62); border-color: rgba(255, 255, 255, .28); }
/* The bad floating one (a refusal the page itself shows, e.g. a file over the limit). 🔴 Until rc000464 the
   JS painted `toast-mal`, which had no rule: white text on an uncoloured card. */
.toast-bad { background: rgba(179, 38, 30, .66); border-color: rgba(255, 255, 255, .28); }

/* The icon's circle. Filled, as in the reference: it is the mark seen before reading. */
.notice-icon {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  margin-top: 1px;
}

.notice-icon-ok  { background: #1f7a4d; }
.notice-icon-bad { background: #b3261e; }
/* On the glassy floating toast the circle is a translucent white disc, not a solid colour (which would
   clash with the coloured card). The inline alert keeps the solid circle. */
.toast .notice-icon-ok, .toast .notice-icon-bad { background: rgba(255, 255, 255, .22); }

.toast-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* The headline carries the state colour; the explanation, the usual grey. That way they are told
   apart without needing different sizes. */
.toast-headline { font-size: 14px; font-weight: 700; overflow-wrap: anywhere; }
/* On the floating toast the text is white (over the coloured glass); the inline alert keeps dark red. */
.toast-ok .toast-headline, .toast-bad .toast-headline { color: #fff; }
.page-alert .toast-headline { color: #9c1f19; }

.toast-rest { color: var(--text-soft); font-size: 13.5px; overflow-wrap: anywhere; }
.toast .toast-rest { color: rgba(255, 255, 255, .92); }

.toast-x,
.alert-x {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: 17px;
  line-height: 1;
  padding: 0 2px;
  color: var(--text-faint);
}
.toast-x:hover, .alert-x:hover { color: var(--text-main); }
/* White close on the coloured glass. */
.toast .toast-x { color: rgba(255, 255, 255, .85); }
.toast .toast-x:hover { color: #fff; }

/* On leaving: it fades upward instead of disappearing abruptly, so it is seen THAT it went and it
   does not look like the screen blinked. */
.toast.is-leaving {
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity .35s ease, transform .35s ease;
}

@media (prefers-reduced-motion: reduce) {
  .toast.is-leaving { transition: opacity .35s ease; transform: none; }
}

/* On a phone they take the full width: at 320 px a 460 box spills off the side. */
@media (max-width: 520px) {
  .toasts {
    left: max(10px, env(safe-area-inset-left));
    right: max(10px, env(safe-area-inset-right));
    width: auto;
  }
}

/* A cell with context data —who and when— inside a narrow table.

   🔴 An email is ONE WORD for the browser: `sistemas@powerlink.es` measures what it measures and
   does not break on its own, so it sets a minimum width the column cannot go below. In a request's
   record that was enough for the quotes table not to fit and for a horizontal scrollbar to appear,
   leaving «Abrir →» out of view — the only column you do anything with. */
.cell-meta { min-width: 0; }

/* ---------- The message sent to the customer, in the send history ----------

   🔑 It goes in ITS OWN ROW, full width. In one of the cells above it would end up in a two-finger
   column, broken into ten lines, and what you want here is to read it just as it went out.

   The send's row loses its bottom rule when it carries a message: the two rows are ONE thing, and a
   rule in between would separate them as if they were two different sends. */

.table tr.fila-sin-linea > td { border-bottom: 0; }

.fila-mensaje > td { padding-top: 0; }

.mensaje-etiqueta {
  display: block;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-faint);
  margin-bottom: 4px;
}

.mensaje-enviado {
  border-left: 3px solid var(--border-2);
  padding: 2px 0 2px 10px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-soft);
  white-space: normal;
}
.mensaje-enviado p { margin: 0 0 6px; }
.mensaje-enviado p:last-child { margin-bottom: 0; }

/* A checkbox with its phrase beside it, on one line. It is the neutral way to offer an option that
   is almost always left as is: with no box of its own nor title, so it does not compete with the
   field it accompanies. The `gap` and not a margin on the input: this way a click on the phrase
   also ticks it. */
.check-simple {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13.5px;
  color: var(--text-soft);
  cursor: pointer;
  user-select: none;
}
.check-simple input { accent-color: var(--navy); }

/* Taxonomy management — TREE view: categories with their families nested underneath. Inputs use the
   house `.row-form`; the SELECTS get the same field box here because the house `.row-form select` sets
   no border/background (so on this screen every drop would render with the browser's default chrome
   next to a bordered input). Scoped to `.tax-tree` so other row-form selects keep their own sizing. */
.tax-add { margin: 4px 0 16px; flex-wrap: wrap; }
.tax-add input { flex: 0 1 220px; }
.tax-tree, .tax-fams { list-style: none; margin: 0; padding: 0; }
.tax-tree { border-top: 1px solid var(--border); }
.tax-cat { border-bottom: 1px solid var(--border); padding: 8px 0; }
.tax-fams { margin: 4px 0 2px 8px; padding-left: 22px; border-left: 2px solid var(--border-2); }
.tax-fam { padding: 2px 0; }

/* Each node is a collapsed <details>: the summary is the clean row (name · counts · «Editar»); the
   actions (rename/move/combine/delete) only appear when you open it. Removes the always-visible
   dropdown clutter. */
.tax-node { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; cursor: pointer; padding: 4px 0; }
.tax-more > summary { list-style: none; }
.tax-more > summary::-webkit-details-marker { display: none; }
.tax-more > summary::marker { content: ""; }
.tax-title { font-weight: 500; }
.tax-cat > .tax-more > summary .tax-title { font-weight: 700; }  /* categories stand out */
.tax-count { color: var(--text-soft); font-size: 13.5px; white-space: nowrap; }
.tax-edit-hint { margin-left: auto; color: var(--navy-soft); font-size: 13.5px; }
.tax-edit-hint::after { content: " ▾"; }
.tax-more[open] > summary .tax-edit-hint::after { content: " ▴"; }
.tax-more-body { display: flex; flex-direction: column; gap: 8px; padding: 4px 0 12px 4px; }
.tax-add-fam { opacity: .9; }
.tax-hint { color: var(--text-soft); font-size: 13.5px; }
.tax-tree select {
  border: 1px solid var(--border-2);
  border-radius: 8px;
  padding: 7px 11px;
  font: inherit;
  font-size: 14px;
  color: var(--text-main);
  background: #fff;
  max-width: 240px;
}
.tax-tree select:focus {
  outline: none;
  border-color: var(--navy-soft);
  box-shadow: 0 0 0 3px rgba(29, 79, 134, .12);
}

/* The "N values" hint on each variety axis chip. */
.variants-count { color: var(--text-soft); font-size: 13px; }

/* ---------- Product gallery (Fase B) in the editor ---------- */
.gallery-thumbs { display: flex; flex-wrap: wrap; gap: 12px; margin: 0 0 14px; }
.gallery-thumb {
  width: 96px; display: flex; flex-direction: column; align-items: center; gap: 4px;
  border: 1px solid var(--border); border-radius: var(--radius); padding: 8px; background: var(--surface);
}
.gallery-thumb img { width: 78px; height: 78px; object-fit: contain; }
/* The primary (card/storefront main) image, chosen from the gallery (rc000180). */
.gallery-thumb.is-primary { border-color: var(--navy-soft, #2d5bb8); box-shadow: 0 0 0 1px var(--navy-soft, #2d5bb8); }
.gallery-thumb .pill-primary { background: var(--navy-soft, #2d5bb8); color: #fff; font-size: 12px;
  font-weight: 600; padding: 1px 8px; border-radius: 10px; }
.gallery-thumb-actions { display: flex; flex-direction: column; align-items: center; gap: 2px; }

/* ---------- Variant matrix (Fase B, Inventory item page) ---------- */
/* Inline controls inside panel tables — the varieties matrix and the documents table's add-row — get the
   house field look, compact (operator 2026-10-02: «no se aplican estilos»: they only had a width, so they
   rendered as raw browser controls). The price tree keeps its own `.price-in` look, hence the exclusion. */
.variants-table:not(.price-tree) :is(input:not([type="file"]):not([type="checkbox"]):not([type="hidden"]), select),
.docs-table :is(input:not([type="file"]):not([type="hidden"]), select) {
  width: 100%; min-width: 74px; border: 1px solid var(--border-2); border-radius: 7px; padding: 6px 9px;
  font: inherit; font-size: 14px; color: var(--text-main); background: #fff;
}
.variants-table:not(.price-tree) :is(input, select):focus,
.docs-table :is(input, select):focus { outline: none; border-color: var(--navy-soft); box-shadow: 0 0 0 3px rgba(29, 79, 134, .12); }
/* A file input reads as a small secondary button + the chosen file name, not the browser's raw control. */
.docs-table input[type="file"], .gallery-upload input[type="file"] { font: inherit; font-size: 12.5px; color: var(--text-soft); }
.docs-table input[type="file"] { max-width: 240px; }
.gallery-upload input[type="file"] { max-width: 130px; }
:is(.docs-table, .gallery-upload) input[type="file"]::file-selector-button {
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--ink); background: #fff; cursor: pointer;
  border: 1px solid var(--border-2); border-radius: 7px; padding: 5px 10px; margin-right: 8px;
}
:is(.docs-table, .gallery-upload) input[type="file"]::file-selector-button:hover { border-color: var(--navy-soft); }
.variants-table td.num { text-align: right; white-space: nowrap; }
.variants-table td .btn-link { margin-left: 8px; font-size: 13px; }
.variants-table td.acciones { white-space: nowrap; }
/* Price tree (decision 11): a template parent row groups its variety children, which indent so the
   table reads as a tree of "model → its varieties". A standalone product renders no parent row. */
.price-tree tr.tree-parent td { background: var(--surface-2, #f6f8fa); font-weight: 600; }
.price-tree tr.tree-child td:first-child { padding-left: 26px; position: relative; }
.price-tree tr.tree-child td:first-child::before {
  content: "└"; position: absolute; left: 10px; color: var(--muted, #8a94a6); font-weight: 400;
}
.price-tree .pill { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 10px;
  font-size: 12px; font-weight: 600; vertical-align: middle; }
.price-tree .pill-off { background: var(--error-bg, #fde8e8); color: var(--error, #b42318); }
.price-tree .pill-warn { background: #fdf0e3; color: var(--warn-dark); }
.price-tree tr.row-hidden td { opacity: 0.6; }
/* Price inputs: the same look as the .field inputs (which these are NOT inside), a touch narrower,
   right-aligned; the «con IVA» hint sits to the right of each price (rc000181). */
.price-tree input.price-in {
  width: 96px; text-align: center; border: 1px solid var(--border-2); border-radius: 8px;
  padding: 7px 9px; font: inherit; font-size: 14px; color: var(--text-main); background: #fff;
  font-variant-numeric: tabular-nums;
}
.price-tree input.price-in:focus { outline: none; border-color: var(--navy-soft); box-shadow: 0 0 0 3px rgba(45,91,184,0.12); }
.price-tree td.price-cell { white-space: nowrap; }
.price-tree .price-vat { margin-left: 8px; color: var(--text-soft, #8a94a6); font-size: 13px; font-variant-numeric: tabular-nums; vertical-align: middle; }
/* One consistent alignment for the whole tree: headers and cells left, content vertically centered,
   so a header always sits above the left edge of its input/value (the number is right-aligned INSIDE
   the input). Overrides the generic `.num` right-align. */
.price-tree th, .price-tree td { text-align: left; vertical-align: middle; }
.price-tree th { white-space: nowrap; }
.price-tree input.price-in { vertical-align: middle; }
.price-tree td.col-published { white-space: nowrap; }
/* Expand chevron for a model with varieties in the Catálogo / Inventario listings (rc000185; since
   rc000356 it sits inside the reference cell, `.list-ref`): rotates from → (collapsed) to ▼ (open). */
.expand-toggle { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
  color: var(--text-soft); border-radius: 6px; text-decoration: none; }
.expand-toggle:hover { background: var(--surface-2); color: var(--text-main); }
.expand-toggle svg { transition: transform .15s ease; }
.expand-toggle[aria-expanded="true"] svg { transform: rotate(90deg); }
/* Inline variety rows injected under a collapsed model (rc000185): real rows that line up with the
   parent columns, tinted and with the name indented under a tree tick. */
.table tr.variety-row > td { background: var(--surface-2, #f6f8fa); }
.table tr.variety-row td.variety-name { padding-left: 28px; position: relative; }
.table tr.variety-row td.variety-name::before { content: "└"; position: absolute; left: 14px; color: var(--muted, #8a94a6); }
.table tr.variety-row td.variety-name a { font-weight: 600; }
/* Variant config warning: variants on but no generating axis yet (no matrix, no storefront dropdown). */
.variants-warn { color: var(--warning, var(--warn-ink)); font-weight: 600; }

/* Variety axis selector (unified card): the attributes that generate varieties, as chips. */
.variants-axes { display: flex; flex-wrap: wrap; gap: 10px; margin: 8px 0 12px; }
.variants-axis { display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; border: 1px solid var(--border); border-radius: var(--radius); cursor: pointer; }
.variants-axis input { margin: 0; }
.variants-axis .variants-count { color: var(--text-soft); font-size: 13px; }

/* Variety switcher (Catalogue product header, operator 2026-10-02): ONE control instead of a chip per
   variety — ‹ › links to the previous/next sibling, the middle is a <details> with a searchable list
   (panel.js: [data-vs-menu]). Scales to models with 20+ sizes. */
.detail-variety { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.vs { position: relative; display: inline-flex; align-items: stretch; border: 1px solid var(--border);
  border-radius: 10px; background: var(--surface); }
.vs-step { display: flex; align-items: center; padding: 0 11px; font-size: 18px; line-height: 1;
  color: var(--navy-soft); text-decoration: none; }
.vs-step:first-child { border-right: 1px solid var(--border); border-radius: 10px 0 0 10px; }
.vs-step:last-child { border-left: 1px solid var(--border); border-radius: 0 10px 10px 0; }
a.vs-step:hover { background: var(--surface-2); }
.vs-step.is-off { color: var(--border-2); cursor: default; }
.vs-menu { position: relative; }
.vs-btn { display: flex; align-items: center; gap: 10px; padding: 7px 12px; cursor: pointer; list-style: none;
  color: var(--ink); white-space: nowrap; }
.vs-btn::-webkit-details-marker { display: none; }
.vs-btn:hover { background: var(--surface-2); }
.vs-label { font-weight: 700; font-variant-numeric: tabular-nums; }
.vs-ref { color: var(--text-soft); font-size: 12.5px; }
.vs-pos { font-size: 12px; font-weight: 600; color: var(--navy-soft); background: var(--tint-blue); border-radius: 999px;
  padding: 1px 8px; font-variant-numeric: tabular-nums; }
.vs-caret { color: var(--text-soft); font-size: 11px; }
.vs-pop { position: absolute; left: 0; top: calc(100% + 6px); z-index: 45; width: min(560px, calc(100vw - 32px));
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 14px 36px rgba(16, 33, 58, .18); overflow: hidden; }
.vs-search { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--border); }
.vs-search input { flex: 1; min-width: 0; font: inherit; font-size: 14px; border: 1px solid var(--border-2);
  border-radius: 8px; padding: 8px 10px; }
.vs-count { font-size: 12px; color: var(--text-soft); white-space: nowrap; font-variant-numeric: tabular-nums; }
.vs-row { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) 84px 92px 18px; gap: 10px;
  align-items: center; padding: 7px 14px; }
.vs-pop.no-price .vs-row { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) 92px 18px; }
.vs-row .num { text-align: right; font-variant-numeric: tabular-nums; }
.vs-head { font-size: 11px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  background: var(--navy); color: #e8eef6; padding-top: 8px; padding-bottom: 8px; }
.vs-list { max-height: 320px; overflow-y: auto; }
.vs-opt { font-size: 13.5px; color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--border); }
.vs-opt:hover, .vs-opt:focus { background: var(--surface-2); outline: none; }
.vs-opt.is-current { background: var(--tint-blue); }
.vs-opt.is-off { color: var(--text-soft); }
.vs-o-label { font-weight: 700; font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vs-o-ref { color: var(--text-soft); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vs-o-zero { color: var(--warning); font-weight: 700; }
.vs-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--success); justify-self: center; }
.vs-dot.is-off { background: transparent; border: 1.5px solid var(--text-soft); }
.vs-empty { padding: 16px 14px; margin: 0; color: var(--text-soft); font-size: 13.5px; }
.vs-foot { padding: 9px 14px; border-top: 1px solid var(--border); font-size: 12.5px; }
.vs-foot a { color: var(--navy-soft); font-weight: 600; text-decoration: none; }

/* Checkout config managers (Ajustes → Pedidos): a row = an inline edit form + its action buttons. */
.checkout-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.checkout-edit { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0; flex: 1 1 auto; }
.checkout-edit input[type="text"], .checkout-edit input:not([type]) { border: 1px solid var(--border-2); border-radius: 8px; padding: 6px 10px; font: inherit; font-size: 13.5px; }
/* Sizing: the IBAN (24 chars) is the wide one; label/provider stay modest. */
.checkout-edit input[name="label"], .checkout-edit input[name="provider"] { flex: 0 1 150px; min-width: 110px; }
.checkout-edit input[name="iban"] { flex: 2 1 260px; min-width: 240px; font-variant-numeric: tabular-nums; }
.checkout-add { margin-top: 12px; }

/* Shipping-method logo in the Pedidos manager. */
.checkout-logo { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.ship-logo { width: 34px; height: 34px; object-fit: contain; background: var(--surface-2); border-radius: 6px; border: 1px solid var(--border); }

/* Discount offers (coupons + automatic promotions). */
.code-chip { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; font-weight: 700;
  padding: 2px 7px; border-radius: 6px; background: var(--surface-2); border: 1px solid var(--border);
  letter-spacing: .04em; }
/* The promo ★ marker in the eligibility picker. Defined in public.css too (the storefront), but the
   admin loads only panel.css, so it needs its own copy here or it renders unstyled. */
.co-star { color: #e2a400; }

/* The offer's scope (rc000422): the two radios, one per line, and the fieldset that holds the pickers.
   A fieldset has a browser border and a min-content minimum width that would let the tables overflow
   the card; while «whole catalogue» is chosen panel.js disables it and it is dimmed. */
.offer-scope { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; margin: 4px 0 6px; }
.offer-selection { border: 0; margin: 0; padding: 0; min-width: 0; }
.offer-selection:disabled { opacity: .45; }

/* Eligibility picker: a filterable, client-paginated table of checkboxes (panel.js
   [data-offer-picker]). The body scrolls so a long catalogue never pushes the save button off-screen;
   pagination hides rows visually only, the checkboxes stay in the form. */
.offer-picker { margin-top: 14px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.offer-picker-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 8px 12px; background: var(--surface-2); border-bottom: 1px solid var(--border); }
.offer-picker-bar strong { flex: 0 0 auto; }
.offer-picker-search { flex: 1 1 12rem; min-width: 0; }
.offer-picker-count { flex: 0 0 auto; font-weight: 600; }
.offer-picker .table-wrap { max-height: 320px; overflow-y: auto; margin: 0; }
.offer-picker-table { margin: 0; }
.offer-picker-table th.offer-check, .offer-picker-table td.offer-check { width: 2.5rem; text-align: center; }
.offer-picker-table td.offer-check input { margin: 0; }
tr[data-offer-row][hidden] { display: none; }
.offer-picker-pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px; padding: 8px 12px; background: var(--surface-2); border-top: 1px solid var(--border); }
.offer-picker-nav { display: inline-flex; align-items: center; gap: 8px; }
/* Two square arrow buttons; the page indicator sits to their RIGHT. */
.offer-pg-btn { display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; padding: 0; font-size: 18px; line-height: 1; border-radius: 6px;
  border: 1px solid var(--border); background: var(--surface); color: inherit; cursor: pointer; }
.offer-pg-btn:hover:not(:disabled) { background: var(--surface-2); }
.offer-pg-btn:disabled { opacity: .4; cursor: default; }
.offer-picker-status { font-variant-numeric: tabular-nums; font-weight: 600; }
.offer-picker-perpage { display: inline-flex; align-items: center; gap: 6px; }
.offer-picker-empty { padding: 12px; margin: 0; }

/* ===== Detail pages (design A, operator 2026-10-02): a flat header with the actions in a top
   toolbar, a card-less body on a WHITE page, sections split by hairline rules instead of cards.
   Applied to the request / quote / order detail screens via the `.detail` wrapper. ===== */
/* White page edge-to-edge: paint the whole body, not just the centred `.content` column, so no grey
   gutters show beside it on wide screens (operator: «sigue con 2 colores»). The navy topbar keeps its
   own background from chrome.css. */
body:has(.detail, .list-page, .home, .set), .content:has(.detail, .list-page, .home, .set) { background: var(--surface); }
/* AI-AGENT: the detail page's shell-agnostic pieces — `.detail-crumbs*`, `.detail-head`, `.detail-id`,
   `.detail-title*`, `.detail-meta*`, `.detail-actions*`, `.detail-figures*`, `.flat-sec*`, `.flat-cols*` and
   `.detail .table th` — MOVED to chrome.css (rc000403): the customer's quote and order pages wear the same
   design and the storefront does not load this sheet. The ⋯ menu (`.detail-menu*`) stays here: only the
   panel has one. chrome.css loads AFTER this file — see the note on the listing, further down. */
/* ⋯ overflow menu — self-contained, driven by registerDropdown's `.open` toggle */
.detail-menu { position: relative; }
.detail-menu-btn { font-size: 16px; line-height: 1; padding: 8px 12px; }
.detail-menu-pop { position: absolute; right: 0; top: calc(100% + 6px); display: none; z-index: 40;
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 10px 30px rgba(16, 33, 58, .14); min-width: 210px; padding: 6px; }
.detail-menu.open .detail-menu-pop { display: block; }
.detail-menu-pop form { margin: 0; }
.detail-menu-pop a, .detail-menu-pop button { display: flex; width: 100%; gap: 10px; align-items: center;
  background: none; border: 0; color: var(--ink); font: inherit; font-size: 14px; text-align: left;
  padding: 9px 10px; border-radius: 8px; cursor: pointer; text-decoration: none; }
.detail-menu-pop a:hover, .detail-menu-pop button:hover { background: var(--surface-2, #f1f5f9); }
.detail-menu-pop .danger { color: var(--error); }
.detail-menu-pop hr { border: 0; border-top: 1px solid var(--border); margin: 5px 2px; }
/* «Añadir variedad» as the last row of the varieties matrix (operator 2026-10-02): a tinted row whose
   cells bind to #addvar by `form=`, instead of a separate box under the table. */
.table .addrow td { background: color-mix(in srgb, var(--tint-blue) 55%, var(--surface)); border-bottom: 0; }
.variants-table .addrow .btn-primary { white-space: nowrap; }
/* Gallery upload as the last tile of the thumbs grid (Catalogue product page, design A). */
.gallery-upload { justify-content: center; text-align: center; border-style: dashed; width: 150px; }
.gallery-upload label { display: flex; flex-direction: column; align-items: center; gap: 2px; font-size: 12.5px; color: var(--text-soft); }
.gallery-upload label b { font-size: 20px; color: var(--navy-soft); line-height: 1; }
/* Documents table (Catalogue product page): kind with a file glyph, language as a code chip, empty state. */
.doc-kind { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; color: var(--ink); }
.doc-kind svg { flex: none; color: var(--navy-soft); }
.lang-chip { display: inline-block; font-size: 11.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--navy-soft); background: var(--tint-blue); border-radius: 6px; padding: 2px 7px; }
.docs-table .empty-row td { color: var(--text-soft); font-size: 13.5px; padding: 14px 12px; }
.docs-table .addrow td { vertical-align: middle; }

/* Shared admin listing (rc000356 — Catálogo + Inventario, operator 2026-10-02: «las 2 tengan el mismo
   diseño de tabla y se vean los nombres completos (Referencia primera col)»): white and card-less like
   the detail pages, FULL WIDTH (the 1360px cap of `.content` is lifted only here — long product names need
   the room), dark table head, reference first and unbroken, names never shortened (they wrap), category ›
   family as a quiet second line, icon actions. Templates: admin/products.html + admin/inventory.html,
   shared cells `list-ref` / `list-art` in common/common.html. */
.content:has(.list-page) { max-width: none; }
/* AI-AGENT: the listing's shell-agnostic pieces — `.list-head*`, `.list-toolbar`, `.filters-admin` (+ its
   inputs), the `.seg*` tabs, `.list-table th`, `.list-ref`, `.empty-admin` — MOVED to chrome.css (rc000401):
   the customer's «Presupuestos» / «Pedidos» wear the same listing and the storefront does not load this
   sheet. chrome.css loads AFTER this file, so a rule here with the same specificity as one of those LOSES;
   to override one from the panel, raise the specificity (as `.list-toolbar .filters-admin` does). */
.list-table tr.row-hidden > td:not(.cell-actions) { opacity: 0.6; }
.expand-gap { display: inline-block; width: 24px; flex: none; }
.list-art { display: flex; align-items: center; gap: 12px; min-width: 300px; }
.list-thumb { width: 38px; height: 38px; flex: none; display: grid; place-items: center; overflow: hidden; border-radius: 7px; background: var(--surface-2); }
.list-thumb img { width: 100%; height: 100%; object-fit: contain; }
.list-art-txt { min-width: 0; }
.list-name { font-weight: 600; color: var(--text-main); line-height: 1.35; }
.list-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; margin-top: 2px; font-size: 12.5px; color: var(--text-soft); }
.vcount { font-size: 11.5px; font-weight: 700; color: var(--navy-soft); background: var(--tint-blue); border-radius: 6px; padding: 1px 7px; }
.list-table .per { display: block; font-size: 12px; color: var(--text-soft); }
.list-table .stock-neg { color: var(--error); font-weight: 700; }
.icon-btn { display: inline-grid; place-items: center; width: 30px; height: 30px; vertical-align: middle; cursor: pointer;
  color: var(--text-soft); background: none; border: 1px solid transparent; border-radius: 7px; }
.icon-btn:hover { color: var(--text-main); background: #fff; border-color: var(--border-2); }
.icon-btn:focus-visible { outline: 2px solid var(--navy-soft); outline-offset: 1px; }

/* Clientes listing (rc000359) on top of the shared listing: status tabs with counts, customer cell with
   initials avatar + full name + tax id / tags, contact stacked, the «Sin cuenta» guests note. */
.text-warn { color: var(--warn-dark); }
.list-note { max-width: 90ch; margin: 0 0 14px; padding: 10px 14px; font-size: 13.5px; color: var(--text-main);
  background: var(--tint-warn); border-left: 3px solid var(--warn-ink); border-radius: 0 9px 9px 0; }
.who { display: flex; align-items: center; gap: 12px; min-width: 260px; }
.who-av { width: 36px; height: 36px; flex: none; display: grid; place-items: center; border-radius: 50%;
  font-size: 12.5px; font-weight: 800; letter-spacing: .02em; color: var(--navy-soft); background: var(--tint-blue); }
.who-av.is-ind { color: var(--ok-ink); background: var(--tint-ok); }
.who-av.is-guest { color: var(--text-soft); background: var(--surface-2); border: 1px dashed var(--border-2); }
.who-tag { font-size: 11px; font-weight: 700; border-radius: 6px; padding: 1px 7px; color: var(--text-soft); background: var(--surface-2); }
.who-tag.is-group { color: var(--navy-soft); background: var(--tint-blue); }
.who-tag.is-ind { color: var(--ok-ink); background: var(--tint-ok); }
.who-contact { font-size: 13.5px; line-height: 1.45; }
.who-contact .muted { display: block; }

/* Sales listings (rc000361 — Solicitudes / Presupuestos / Pedidos): the number cell reuses `.list-ref`,
   the customer cell keeps the full name with the project as a quiet second line. */
.doc-who { min-width: 240px; }

/* Admin home as a work tray (rc000363): white and flat like the detail pages; the «Pendiente» rows are
   links to the filtered listing (number coloured by urgency, zero rows dimmed), the activity feed, the
   best-seller bars and the catalogue health figures. */
.home-actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.home-alert { margin: 14px 0 0; }
/* `.detail-figures.home-figures`, not `.home-figures`: the base `.detail-figures` now loads AFTER this sheet
   (chrome.css, rc000403), so a single class here would lose to it. */
.detail-figures.home-figures { margin: 20px 0 2px; }
.home-figures .fig .d { font-size: 12.5px; font-weight: 600; margin-top: 2px; color: var(--text-soft); }
.home-figures .fig .d.is-up { color: var(--ok-ink); }
.home-figures .fig .d.is-down { color: var(--error); }
.home-note { margin: 4px 0 0; font-size: 12px; color: var(--text-soft); }
.home-grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 0 40px; }
@media (max-width: 960px) { .home-grid { grid-template-columns: 1fr; } }
.home-q { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto 14px; gap: 12px; align-items: center;
  padding: 11px 6px; border-bottom: 1px solid var(--border); color: inherit; }
.home-q:hover { background: var(--surface-2); }
.home-q-n { font-size: 21px; font-weight: 800; text-align: center; font-variant-numeric: tabular-nums; color: var(--text-main); }
.home-q.is-hot .home-q-n { color: var(--warn-ink); }
.home-q.is-bad .home-q-n { color: var(--error); }
.home-q.is-zero .home-q-n, .home-q.is-zero .home-q-t { color: var(--text-soft); font-weight: 600; }
.home-q-txt { min-width: 0; display: flex; flex-direction: column; }
.home-q-t { font-weight: 600; color: var(--text-main); }
.home-q-s { font-size: 12.5px; color: var(--text-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-q-age { font-size: 12px; color: var(--text-soft); white-space: nowrap; }
.home-q-chev { color: var(--text-soft); }
.home-act { display: grid; grid-template-columns: 30px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  padding: 9px 4px; border-bottom: 1px solid var(--border); color: inherit; }
.home-act:hover { background: var(--surface-2); }
.home-act-ic { width: 30px; height: 30px; display: grid; place-items: center; border-radius: 8px; font-size: 11px; font-weight: 800;
  color: var(--navy-soft); background: var(--tint-blue); }
.home-act-ic.is-quote { color: var(--warn-ink); background: var(--tint-warn); }
.home-act-ic.is-order { color: var(--ok-ink); background: var(--tint-ok); }
.home-act-ic.is-customer { color: var(--text-soft); background: var(--surface-2); }
.home-act-txt { min-width: 0; display: flex; flex-direction: column; font-size: 13.5px; color: var(--text-main); }
.home-act-s { font-size: 12px; color: var(--text-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-act-w { font-size: 12px; color: var(--text-soft); white-space: nowrap; }
.home-bars { display: grid; gap: 9px; }
.home-bar { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 10px; align-items: center; font-size: 13.5px; }
.home-bar-n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-main); }
.home-bar-v { font-weight: 700; font-variant-numeric: tabular-nums; }
/* The bar is an SVG with a width ATTRIBUTE: an inline style= would be dropped by the CSP (style-src 'self'). */
.home-bar-track { grid-column: 1 / -1; display: block; width: 100%; height: 8px; border-radius: 99px; overflow: hidden; }
.home-bar-bg { fill: var(--surface-2); }
.home-bar-fill { fill: var(--navy-soft); }
.home-cat { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 640px) { .home-cat { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.home-cat a { display: flex; flex-direction: column; padding: 8px 14px; border-left: 1px solid var(--border); color: inherit; }
.home-cat a:first-child { border-left: 0; padding-left: 4px; }
/* 🔑 EACH FIGURE IS A LINK to the product list already filtered (published, hidden, without image, without
   price) and has to LOOK like one (rc000429, operator: «me indica 29 sin imagen pero no puedo pulsar»): it
   was a link with nothing that said so — no chevron, no change under the pointer — next to a «Pendiente»
   column whose rows have both. Same chevron and same hover as `.home-q`. */
.home-cat a:hover { background: var(--surface-2); }
.home-cat a:focus-visible { outline: 2px solid var(--navy-soft); outline-offset: -2px; }
.home-cat .l::after { content: " ›"; color: var(--text-soft); }
.home-cat a:hover .l, .home-cat a:hover .l::after { color: var(--text-main); }
.home-cat .v { font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; }
.home-cat .l { font-size: 12px; color: var(--text-soft); }
.flat-sec-head .more-link { margin-left: auto; }
.detail-figures.home-figures .fig .v { font-size: 23px; } /* doubled class: see `.detail-figures.home-figures` above */

/* Category look picker (Catálogo → Categorías, rc000372). */
.cat-featured { font-size: 11.5px; font-weight: 700; color: var(--warn-ink); background: var(--tint-warn); border-radius: 6px; padding: 1px 7px; }
.cat-look { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; padding: 10px 0 12px; border-bottom: 1px solid var(--border); margin-bottom: 10px; }
.cat-look-h { width: 100%; font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-soft); }
.icon-pick { display: flex; flex-wrap: wrap; gap: 4px; max-width: 520px; }
.icon-opt { position: relative; display: grid; place-items: center; width: 34px; height: 34px; padding: 6px; border: 1px solid var(--border); border-radius: 8px;
  color: var(--text-soft); cursor: pointer; background: #fff; }
.icon-opt input, .tint-opt input { position: absolute; opacity: 0; pointer-events: none; }
.icon-opt:has(input:checked) { color: var(--t); background: var(--s); border-color: var(--t); }
.icon-opt:has(input:focus-visible), .tint-opt:has(input:focus-visible) { outline: 2px solid var(--navy-soft); outline-offset: 1px; }
.tint-pick { display: flex; gap: 6px; align-items: center; }
.tint-opt { position: relative; cursor: pointer; }
.tint-opt span { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: var(--t); border: 2px solid #fff;
  box-shadow: 0 0 0 1px var(--border-2); font-size: 11px; font-weight: 800; color: var(--text-soft); }
.tint-auto span { background: #fff; }
.tint-opt:has(input:checked) span { box-shadow: 0 0 0 2px var(--navy); }
.cat-look-check { display: inline-flex; align-items: center; gap: 6px; font-size: 13.5px; font-weight: 600; }

/* ===== Settings as a FLAT page (rc000431, operator 2026-10-07: «hazme un diseño de ajustes con página
   plana» → «aplícalo»). No cards: every section is a strip — its title and one line about it on the
   left, its settings on the right, split by hairlines — on the same white page as the home and the
   detail screens (`.set` is in the `body:has(...)` rule above).

   🔑 ONE control per kind of answer, on every tab, which is what the screen lacked (it had pills,
   bare ticks and a list side by side for the same yes/no):
     yes / no      → `.switch`, at the right end of its row;
     one of a few  → a `<select>` in `.set-ctl`;
     something typed → an `<input>` / `<textarea>` in `.set-ctl`.

   AI-AGENT: a switch IS the checkbox it always was — same `name`, same `value="1"`, the hidden «0» still
   in front — only drawn differently, so the forms post exactly what they posted and `panel.js` finds its
   fields. Do not swap it for a <button>: an unticked button sends nothing and there would be no «0». ===== */
:root { --rule-soft: #eef1f5; --switch-off: #7d8896; --switch-on: #1c4c7c; }

.set-sec { display: flex; flex-wrap: wrap; gap: 12px 56px; padding: 30px 0 28px; border-top: 1px solid var(--border); }
.set-sec-first { border-top: 0; padding-top: 10px; }
.set-aside { flex: 1 1 220px; max-width: 280px; min-width: 0; }
.set-aside h2 { margin: 0; font-size: 16px; color: var(--ink); }
.set-aside .help { margin: 6px 0 0; font-size: 13.5px; color: var(--text-soft); }
.set-body { flex: 999 1 652px; min-width: 0; display: flex; flex-direction: column; }
.set-body h3 { margin: 24px 0 8px; font-size: 14.5px; color: var(--ink); }
.set-body > :first-child { margin-top: 0; }
.set-body .table-wrap { margin: 0; padding: 0; }
/* A section whose body needs the whole width (the message editors): the title goes on top. */
.set-sec-wide { display: block; }
.set-sec-wide .set-aside { max-width: none; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; margin-bottom: 14px; }
.set-sec-wide .set-aside .tpl-head-actions { margin-left: auto; }
.set-sec-wide .set-aside .muted { font-size: 13.5px; }

.set-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 32px; padding: 14px 0; border-top: 1px solid var(--rule-soft); }
.set-body > .set-row:first-child { border-top: 0; padding-top: 0; }
.set-cap + .set-row, .set-cap + .help + .set-row { border-top: 0; }
.set-row-top { align-items: flex-start; }
.set-text { flex: 1 1 280px; min-width: 0; }
.set-text > label { display: block; font-size: 14.5px; font-weight: 600; color: var(--text-main); }
.set-text .help { margin: 3px 0 0; font-size: 13px; color: var(--text-soft); }
.set-ctl { flex: 0 1 340px; min-width: 0; }
.set-ctl.field { margin-bottom: 0; }
.set-short { max-width: 140px; }
@media (max-width: 699px) { .set-ctl { flex-basis: 100%; } }

/* The switch row does not wrap: the switch stays at the right end of its sentence. */
.set-row-switch { flex-wrap: nowrap; gap: 8px 24px; padding: 10px 0; }
.set-row-switch .set-text > label { cursor: pointer; }
.switch {
  -webkit-appearance: none; appearance: none; flex: none; position: relative; margin: 0;
  width: 44px; height: 26px; border-radius: 999px; background: var(--switch-off);
  cursor: pointer; transition: background .15s;
}
.switch::before {
  content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(10, 37, 64, .35); transition: transform .15s;
}
/* 🔴 On is a FIXED colour (`--switch-on`), not the house's accent (rc000432). With the accent —which
   Settings → Design lets the house pick freely— a pale yellow or orange made «on» lighter than «off»:
   the grey switches read as the active ones, and the focus ring, drawn in the same colour, vanished.
   A state must not depend on a colour the tenant chooses. */
.switch:checked { background: var(--switch-on); }
.switch:checked::before { transform: translateX(18px); }
.switch:focus-visible { outline: 2px solid var(--text-main); outline-offset: 2px; }
.switch:disabled { opacity: .45; cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) { .switch, .switch::before { transition: none; } }
/* A contrast theme repaints backgrounds: there the native tick says more than a track nobody can see. */
@media (forced-colors: active) {
  .switch { -webkit-appearance: auto; appearance: auto; width: auto; height: auto; }
  .switch::before { display: none; }
}
/* The same switch inside a one-line form of a list (a shipping method's «Activo»). */
.switch-inline { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--text-soft); cursor: pointer; }

/* A caption over a group of rows («Al cliente», «Tipos de cuenta que se aceptan»). */
.set-cap { padding: 18px 0 4px; border-top: 1px solid var(--rule-soft); font-size: 12px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--text-soft); }
.set-body > .set-cap:first-child { border-top: 0; padding-top: 0; }
.set-cap + .help { margin: 0 0 4px; }
.set-warn { display: inline-block; margin: 8px 0 0; font-size: 13px; font-weight: 600; color: #7a4a00;
  background: var(--warning-bg); border-radius: 6px; padding: 4px 10px; }
.set-badge { margin-left: 6px; font-size: 12px; font-weight: 700; color: var(--text-main); background: var(--rule-soft);
  border-radius: 6px; padding: 2px 8px; white-space: nowrap; }
.set-row.is-off .set-text > label { color: var(--text-soft); cursor: default; }
.set-empty { margin: 0 0 12px; font-size: 14.5px; color: var(--text-main); }
/* The one-line forms of the managed lists (shipping methods, bank accounts) inside the narrower flat
   column: the order box gets the same look as its neighbours (a `type="number"` the list's own rule does
   not reach) and stays narrow, so «Eliminar» stays on the row. */
.set-body .checkout-row > .checkout-edit { flex: 1 1 0; min-width: 0; }
.set-body .checkout-edit input[name="label"], .set-body .checkout-edit input[name="provider"] { flex: 0 1 132px; min-width: 96px; }
.set-body .checkout-edit input[name="iban"] { flex: 2 1 200px; min-width: 200px; }
.set-body .checkout-edit input[name="holder"] { flex: 1 1 130px; min-width: 110px; }
.set-body .checkout-edit input[type="number"] { width: 62px; border: 1px solid var(--border-2); border-radius: 8px; padding: 6px 8px; font: inherit; font-size: 13.5px; }
.set-body code.copiable { overflow-wrap: anywhere; }
.set-body > .help { margin: 0 0 10px; }
.set-body > .form-actions { margin-top: 14px; }

/* ONE «Guardar» for a form, at the foot of its sections. */
/* 🔑 It STAYS IN VIEW while its form is on screen (rc000432): a switch moves when it is clicked and looks
   done, but nothing is stored until this button — which in General was several screens further down. */
.set-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 10px 16px;
  padding: 14px 0; border-top: 1px solid var(--border); position: sticky; bottom: 0; z-index: 1; background: var(--surface); }
/* The section right after a form starts under that form's save bar, which already draws the line. */
form + .set-sec { border-top: 0; }
.set-foot .help { margin: 0 auto 0 0; }

/* ===== The photos and documents of the catalogue (rc000434, operator 2026-10-08: Medios fused into
   Catálogo — «catálogo pero segmentado en pestañas»; «las fotos… en modo cards pero los pdf… con una tabla
   en lista»). Everything is the shared flat listing (`.list-page`, `.tabs`, `.seg`, the search form, the
   table, the paginator); what is its own is below: the product ROW of the «sin foto / sin ficha» work
   list, the photo CARD, and the page of one file.

   AI-AGENT: the classes keep the name `media-` (photos and documents ARE the media of the catalogue);
   there is no «Media» screen any more. No pills and no dropdown with the whole catalogue in a row. ===== */
/* «Les falta», beside the status filter of the product list. */
.seg-label { font-size: 13px; font-weight: 600; color: var(--text-soft); margin-left: 6px; }
.gap-cell { white-space: nowrap; }
/* 🔴 Two classes on purpose: chrome.css loads AFTER this sheet and its `.btn-mini` would win a tie. */
.btn-mini.btn-mini-primary { background: var(--navy); border-color: var(--navy); color: #fff; }
.btn-mini.btn-mini-primary:hover { background: var(--navy-light); border-color: var(--navy-light); color: #fff; }
/* The search box of the library and of a file's page: wide enough for its placeholder. */
.filters-admin.filters-wide input[type="search"] { min-width: min(420px, 100%); }

/* One end of an assignment is chosen and the other is being looked for in this list. */
.media-mode { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin: 0 0 14px; padding: 10px 14px;
  border-radius: 10px; background: var(--tint-blue); color: var(--text-main); font-size: 14.5px; }
.media-mode span { flex: 1 1 260px; min-width: 0; overflow-wrap: anywhere; }

.media-rows { display: flex; flex-direction: column; }
.media-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; padding: 14px 0; border-top: 1px solid var(--rule-soft); }
.media-rows .media-row:first-child { border-top: 0; }
/* The photo slot: the photo, or a dashed frame that reads «nothing here yet». */
.media-slot { flex: none; position: relative; width: 64px; height: 64px; display: grid; place-items: center; overflow: hidden; border-radius: 10px;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--switch-off); }
.media-slot.is-empty { background: none; border: 1.5px dashed #a9b3c0; }
.media-slot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.media-slot svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.media-id { flex: 1 1 300px; min-width: 0; }
.media-id .list-name { overflow-wrap: anywhere; }
.media-id .list-sub span + span::before { content: "· "; }
.media-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 7px 0 0; }
/* What is there, what is missing, who uses it. Told apart by lightness and by words, not by hue alone. */
.media-tag { font-size: 12.5px; font-weight: 600; border-radius: 6px; padding: 2px 8px; white-space: nowrap; }
.media-tag.is-ok { color: var(--ok-ink); background: var(--success-bg); }
.media-tag.is-missing { color: #7a4a00; background: var(--warning-bg); }
.media-tag.is-used { color: var(--switch-on); background: var(--tint-blue); }
.media-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.media-upload { cursor: pointer; }
/* The real control is a file input clipped to one pixel inside this label: without this the keyboard
   focus was on it and nothing on screen said so. */
.media-upload:focus-within { outline: 2px solid var(--navy-soft); outline-offset: 2px; }
/* The library files whose name resembles the product: seen before deciding — the name is not enough to
   tell whether it is the right product, the photo is. */
.media-similar { flex: 1 1 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin-left: 82px;
  padding: 10px 12px; border-radius: 10px; background: var(--surface-2); }
.media-similar-intro { font-size: 13px; color: var(--text-soft); }
.media-candidate { display: inline-flex; align-items: center; gap: 8px; margin: 0; }
.media-candidate img { width: 44px; height: 44px; object-fit: contain; background: #fff; border: 1px solid var(--border); border-radius: 8px; }
.media-candidate-name { font-size: 13px; color: var(--text-main); max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 640px) { .media-similar { margin-left: 0; } .media-actions { flex-basis: 100%; } }

.media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 16px; }
.media-card { display: flex; flex-direction: column; min-width: 0; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--surface); }
/* 🔴 The picture is laid OVER its box (absolute), not inside its flow: as a child with `height: 100%` it
   was as tall as it was wide and ran over the file's name. */
.media-card-view { position: relative; display: grid; place-items: center; aspect-ratio: 4 / 3; overflow: hidden; background: var(--surface-2); color: var(--text-soft); }
.media-card-view img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.media-card-body { display: flex; flex-direction: column; gap: 5px; padding: 10px 12px 12px; min-width: 0; flex: 1; }
.media-card-name { font-size: 13.5px; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.media-card-meta { font-size: 12.5px; color: var(--text-soft); }
.media-card-body .media-tags { margin: 0; }
.media-card-body form { margin: auto 0 0; }
.media-card-action { margin-top: auto; align-self: flex-start; }
.media-card-body form .media-card-action { width: 100%; }
.media-total { margin: 16px 0 0; }

/* The documents, as a table: a PDF has no thumbnail to show. */
.doc-table .doc-name { min-width: 240px; overflow-wrap: anywhere; }
.doc-table .cell-actions form { display: inline; }

/* ── The page of one file ── */
.file-page { max-width: 1120px; }
.file-back { margin: 0 0 14px; }
.file-cols { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 24px 36px; }
.file-preview { flex: 1 1 320px; min-width: 0; max-width: 480px; }
.file-preview-box { position: relative; display: grid; place-items: center; aspect-ratio: 1 / 1; overflow: hidden; border: 1px solid var(--border);
  border-radius: 12px; background: var(--surface-2); }
.file-preview-box img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.file-preview-box.is-pdf { aspect-ratio: 4 / 3; background: #f6eeee; color: #8f2f2f; font-size: 28px; font-weight: 700; letter-spacing: .08em; }
.file-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.file-info { flex: 1 1 380px; min-width: 0; }
.file-name { margin: 0; font-size: 20px; color: var(--ink); overflow-wrap: anywhere; }
.file-meta { margin: 4px 0 0; font-size: 14px; color: var(--text-soft); }
.file-h { margin: 26px 0 6px; font-size: 13px; font-weight: 700; color: var(--text-soft); text-transform: uppercase; letter-spacing: .04em; }
.file-uses { list-style: none; margin: 0; padding: 0; }
.file-uses li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding: 10px 0; border-top: 1px solid var(--rule-soft); }
.file-uses li:first-child { border-top: 0; }
.file-use-id { flex: 1 1 240px; min-width: 0; }
.file-use-id .list-name { overflow-wrap: anywhere; }
.file-use-id .list-sub span + span::before { content: "· "; }
.file-uses form { margin: 0; }
.file-search { margin: 0 0 6px; }
.file-results { border: 1px solid var(--border); border-radius: 10px; padding: 0 12px; }

/* ---------- Usuarios: the account page, the department page and their lists (rc000438) ----------
   The pages reuse «Mi perfil» (`.detail*`, `.flat-sec`, `.flat-cols`, `.reg-grid` in chrome.css) and the
   switches of Ajustes (`.set-row-switch`, `.switch`); only what is new is here. */
.account-page .detail-actions { margin-left: auto; align-self: center; }
.account-page .detail-actions .btn-secondary, .account-page .detail-actions .btn-danger { font-size: 14.5px; padding: 9px 18px; border-radius: 8px; }
.account-page .flat-cols > div { min-width: 0; }
.account-page .def-list { margin: 14px 0 0; }
.account-note { margin: 12px 0 0; }
.opt-rows > .set-row:first-child { border-top: 0; padding-top: 0; }
.pick-title { margin: 18px 0 2px; font-size: 12.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-soft); }
/* A «what it has + add» list. 🔴 The `[hidden]` rules are needed: these boxes set their own `display`,
   which beats the browser's rule for the attribute, and a hidden row would stay on screen. */
.pick-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--rule-soft); }
.pick-row[hidden], .pick-option[hidden], .pick-add [hidden] { display: none; }
.pick-text { min-width: 0; }
.pick-text strong { display: block; font-size: 14.5px; font-weight: 600; color: var(--text-main); }
.pick-text span { display: block; font-size: 13px; color: var(--text-soft); }
.pick-empty { margin: 4px 0 0; font-size: 14px; color: var(--text-soft); }
.pick-dirty { margin: 10px 0 0; font-size: 13px; font-weight: 600; color: var(--warn-ink); }
.pick-add { margin-top: 12px; }
.pick + .help { margin-top: 12px; }
/* The window that offers what is left: a search box, a list that scrolls on its own, the buttons below. */
.modal-pick { width: min(540px, 100%); display: flex; flex-direction: column; overflow: hidden; }
.modal-pick h2 { padding-right: 36px; }
.pick-search { width: 100%; border: 1px solid var(--border-2); border-radius: 8px; padding: 10px 12px; font: inherit; font-size: 14.5px; color: var(--text-main); background: var(--surface); }
.pick-options { margin-top: 12px; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); overflow-y: auto; max-height: min(46dvh, 360px); min-height: 80px; }
.pick-option { display: flex; align-items: flex-start; gap: 12px; padding: 12px 2px; border-bottom: 1px solid var(--rule-soft); cursor: pointer; }
.pick-option:last-of-type { border-bottom: 0; }
.pick-option input { flex: none; width: 18px; height: 18px; margin: 2px 0 0; accent-color: var(--navy-soft); }
.pick-none { margin: 0; padding: 18px 2px; font-size: 14px; color: var(--text-soft); }
.pick-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding-top: 16px; }
.pick-foot .muted { margin-right: auto; font-size: 13.5px; }
/* The departments of a row of the accounts list, and the note beside «Nuevo departamento». */
.dept-tag { display: inline-block; margin: 1px 2px 1px 0; padding: 2px 8px; border-radius: 6px; background: var(--tint-blue); color: var(--navy-soft); font-size: 12.5px; font-weight: 600; white-space: nowrap; }
.list-intro { max-width: 90ch; margin: 0 0 14px; }

/* ---------- Usuarios › Roles (rc000440): the list, a role's page and the comparison ---------- */
.role-for { max-width: 62ch; }
.role-denied-note { display: block; margin-top: 4px; font-size: 13px; color: var(--warn-dark); }
.role-scope { display: flex; align-items: center; gap: 10px; min-width: 190px; white-space: nowrap; font-size: 13px; color: var(--text-soft); }
/* How much of the panel a role opens. A native <progress>: its width cannot come from a `style`
   attribute (the CSP allows none), and it says its value to a screen reader by itself. */
.role-meter { flex: 1 1 90px; min-width: 70px; height: 8px; border: 0; border-radius: 999px; background: var(--border); overflow: hidden; -webkit-appearance: none; appearance: none; }
.role-meter::-webkit-progress-bar { background: var(--border); border-radius: 999px; }
.role-meter::-webkit-progress-value { background: var(--switch-on); border-radius: 999px; }
.role-meter::-moz-progress-bar { background: var(--switch-on); border-radius: 999px; }
.role-lead { margin: 6px 0 0; max-width: 78ch; font-size: 14.5px; color: var(--text-main); }
.role-cols { display: flex; flex-wrap: wrap; gap: 26px 48px; }
.role-main { flex: 999 1 620px; min-width: 0; }
.role-side { flex: 1 1 300px; min-width: 0; }
.role-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0 0 6px; font-size: 13px; color: var(--text-soft); }
.role-area { display: flex; flex-wrap: wrap; gap: 6px 24px; padding: 14px 0; border-top: 1px solid var(--rule-soft); }
.role-area h3 { flex: 0 0 190px; margin: 0; font-size: 14.5px; font-weight: 600; color: var(--text-main); }
.role-caps { flex: 1 1 340px; min-width: 0; margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.role-caps li { display: flex; align-items: baseline; gap: 10px; }
.role-cap { flex: none; display: inline-block; min-width: 78px; text-align: center; padding: 2px 9px; border-radius: 6px; border: 1px solid transparent; font-size: 12.5px; font-weight: 600; white-space: nowrap; }
.role-cap.is-yes { background: var(--tint-ok); color: var(--ok-ink); border-color: #cfeadb; }
.role-cap.is-no { background: var(--surface); color: var(--text-soft); border-color: var(--border); font-weight: 500; }
.role-cap.is-denied { background: var(--tint-warn); color: var(--warn-dark); border-color: #f3d3b4; }
.role-cap-text { font-size: 14px; color: var(--text-main); }
.role-cap-text.is-no { color: var(--text-soft); }
.role-why { flex: 1 1 100%; margin: 2px 0 0; font-size: 13px; color: var(--warn-dark); }
.role-rules { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--border); }
.role-rules summary { font-weight: 600; color: var(--ink); cursor: pointer; }
.role-rules .help { margin: 8px 0 12px; }
.role-person { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-top: 1px solid var(--rule-soft); }
.role-person:first-of-type { border-top: 0; }
.role-person .muted { display: block; font-size: 13px; }
.role-person .who-av img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.role-box { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--border); }
.role-box .help { margin: 0; }
/* The comparison: one narrow, centred column per role; the areas as full-width group rows. */
.role-matrix th.role-col, .role-matrix td.role-cell { text-align: center; }
.role-matrix th.role-col a { color: inherit; }
.role-matrix th.role-col small { display: block; font-weight: 400; text-transform: none; letter-spacing: 0; opacity: .8; }
.role-matrix tr.role-group th { background: var(--bg); color: var(--ink); text-transform: none; letter-spacing: 0; font-size: 13px; border-radius: 0; text-align: left; }
.role-mark { display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; font-size: 13px; font-weight: 700; }
.role-mark.is-yes { background: var(--success); color: #fff; }
.role-mark.is-no { color: var(--text-faint); }
/* The editor of a role of the house and the page a role is created from (rc000441). */
.role-edit-title { margin-top: 26px; }
.role-main .reg-grid .field input { width: 100%; }
.role-edit-area { padding: 14px 0 6px; border-top: 1px solid var(--rule-soft); }
.role-edit-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 2px; }
.role-edit-head h3 { margin: 0; font-size: 14.5px; font-weight: 600; color: var(--text-main); }
.role-edit-head .muted { font-size: 13px; }
.role-edit-quick { margin-left: auto; display: flex; gap: 6px; }
.role-edit-row { border-top: 0; padding: 7px 0; }
.role-edit-row .set-text > label { font-weight: 500; }
.role-edit-care { color: var(--warn-dark); }
.role-warn { margin: 0; padding: 12px 14px; border-radius: var(--radius); background: var(--tint-warn); color: var(--warn-dark); font-size: 13.5px; }
.role-warn-top { margin: 0 0 12px; }
/* Under a help line (the notices of an account or of «Mi perfil»): it needs air above, and below when a button follows. */
.help + .role-warn { margin: 10px 0 14px; }
.role-new { max-width: 720px; }
.role-bases { border: 1px solid var(--border); border-radius: var(--radius); }
.role-base { display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px; border-bottom: 1px solid var(--rule-soft); cursor: pointer; }
.role-base:last-child { border-bottom: 0; }
.role-base input { flex: none; width: 18px; height: 18px; margin: 2px 0 0; accent-color: var(--navy-soft); }
.role-base strong { display: block; font-weight: 600; color: var(--text-main); }
.role-base span span { display: block; font-size: 13px; color: var(--text-soft); }

/* ---------- Guided tutorials: the «Modo tutorial» window (the list below). The layer itself — dimming, halo,
   bubble — lives in static/css/tour.css since rc000512, because it also runs on the shop's product page for staff. */
/* Ten tutorials (rc000511): two columns where there is room, so the window holds them without scrolling; one
   column on a phone. A group heading spans the row. */
.tour-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: 10px; margin-top: 12px; }
.tour-group { grid-column: 1 / -1; margin: 8px 0 -2px; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted, #5b6b7d); }
.tour-group:first-of-type { margin-top: 0; }
#modal-tours .modal-box { width: min(760px, calc(100vw - 32px)); max-width: none; }
.tour-item {
  display: grid;
  align-content: start;
  gap: 4px;
  text-align: left;
  width: 100%;
  padding: 14px 16px;
  font: inherit;
  color: var(--text-main);
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
  cursor: pointer;
}
.tour-item:hover, .tour-item:focus-visible { border-color: var(--navy-soft); background: var(--tint-blue); }
.tour-item strong { color: var(--ink); font-size: 15px; }
.tour-item .muted { font-size: 13px; line-height: 1.45; }
