/* ===========================================================================
   COMPONENTS — the design system core, shared by every theme
   ---------------------------------------------------------------------------
   Rules here read tokens and nothing else: no literal colour, no literal blur.
   That discipline is what makes a second theme cheap, and it is checkable:

     grep -nE '#[0-9a-fA-F]{3,8}|rgba?\(|blur\([^v]' assets/core/*.css

   Every selector is prefixed with html[data-theme]. That one extra attribute
   selector is a specificity weapon: it beats modules/theme_style's
   `.admin .sidebar` and `.admin #header`, and it beats the Bootstrap 3 bundled
   in assets/builds/vendor-admin.css, which we cannot edit. Same technique
   docs/DARK_MODE.md records for .nav-tabs > li.active > a.
   =========================================================================== */

/* --- the decorative layer everything else shows through -------------------
   #wrapper paints --crm-canvas opaquely and would hide it, so it goes
   transparent and body carries the backdrop. Fixed attachment keeps the
   gradient still while content scrolls. --------------------------------- */

html[data-theme] body.admin {
  /* Color e imagen por separado: un tema con --thm-backdrop: none seguiria
     pintando nada y dejaria ver el lienzo del navegador, que en modo oscuro
     del sistema es negro. */
  background-color: var(--crm-canvas);
  background-image: var(--thm-backdrop);
  background-attachment: fixed;
}

html[data-theme] body.admin #wrapper,
html[data-theme] body.admin .content {
  background: transparent;
}

/* --- level 1: surface — tint, hairline and top highlight, never blurred --- */

/* Scoped to the main sidebar by id, not by .sidebar: #setup-menu-wrapper wears
   the same class but is an overlay that slides over this one (z-index 9,
   style.css:619). Tinting it translucent lets the menu underneath show
   through and the two read as one scrambled list. */
html[data-theme] aside#menu.sidebar {
  background: var(--thm-surface-fill);
  border-inline-end: var(--thm-border-width) solid var(--thm-surface-stroke);
  box-shadow: inset 1px 0 0 var(--thm-surface-highlight);
}

/* The setup menu must fully hide what it covers, so it stays OPAQUE. Blurring
   it would still show the sidebar behind, just softened -- worse for a menu
   than for a modal. It also carries .animated, and an animating opacity or
   transform disables a descendant's backdrop-filter anyway. */
html[data-theme] #setup-menu-wrapper.sidebar {
  background: var(--crm-surface);
  border-inline-end: var(--thm-border-width) solid var(--thm-floating-stroke);
  box-shadow: var(--thm-floating-shadow);
}

html[data-theme] #header {
  background: var(--thm-surface-fill);
  border-bottom: var(--thm-border-width) solid var(--thm-surface-stroke);
  box-shadow: inset 0 1px 0 var(--thm-surface-highlight);
}

html[data-theme] .panel_s,
html[data-theme] .panel {
  background: var(--thm-surface-fill);
  border: var(--thm-border-width) solid var(--thm-surface-stroke);
  border-radius: var(--thm-surface-radius);
  box-shadow: inset 0 1px 0 var(--thm-surface-highlight), var(--thm-surface-shadow);
}

/* The panel's own children paint opaque backgrounds of their own; let the
   panel's tint show through instead of stacking a second surface on top. */
html[data-theme] .panel_s > .panel-body,
html[data-theme] .panel > .panel-body,
html[data-theme] .panel_s > .panel-heading,
html[data-theme] .panel > .panel-heading,
html[data-theme] .panel_s > .panel-footer,
html[data-theme] .panel > .panel-footer {
  background: transparent;
}

html[data-theme] .panel_s > .panel-heading,
html[data-theme] .panel > .panel-heading {
  border-bottom-color: var(--thm-surface-stroke);
}

/* --- sidebar navigation ---------------------------------------------------- */

html[data-theme] #side-menu > li > a {
  border-radius: var(--thm-control-radius);
  transition: background var(--thm-motion) ease, color var(--thm-motion) ease;
}

html[data-theme] #side-menu > li > a:hover,
html[data-theme] #side-menu > li > a:focus {
  background: var(--thm-hover-fill);
}

html[data-theme] #side-menu > li.active > a {
  background: var(--thm-active-fill);
  box-shadow: inset 0 1px 0 var(--thm-surface-highlight);
}

/* --- buttons ---------------------------------------------------------------
   No transform on hover: a transform on an ancestor disables backdrop-filter
   on its descendants, and scaling controls inside a dense table reads badly. */

html[data-theme] .btn {
  border-radius: var(--thm-control-radius);
  transition: background var(--thm-motion) ease, border-color var(--thm-motion) ease,
              box-shadow var(--thm-motion) ease;
}

html[data-theme] .btn-default {
  background: var(--thm-elevated-fill);
  border-color: var(--thm-surface-stroke);
  color: var(--crm-ink-2);
  box-shadow: inset 0 1px 0 var(--thm-surface-highlight);
}

html[data-theme] .btn-default:hover,
html[data-theme] .btn-default:focus {
  background: var(--thm-hover-fill);
  border-color: var(--thm-elevated-stroke);
  color: var(--crm-ink);
}

html[data-theme] .btn-default:active,
html[data-theme] .btn-default.active {
  background: var(--thm-active-fill);
}

html[data-theme] .btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--thm-focus-ring);
}

html[data-theme] .btn.disabled,
html[data-theme] .btn[disabled] {
  opacity: var(--thm-disabled-opacity);
}

/* --- form controls --------------------------------------------------------- */

/* A field is a well. Painted with the elevated fill it sat 1.03:1 from a panel
   and 1.13:1 from a modal card -- the form read as one flat wash and only the
   focused field could be told apart. Its own tokens fix that, and the border
   carries most of the separation: swapping the fill for the sunken one alone
   lands at the same distance from the card, just on the other side. */
html[data-theme] .form-control {
  background: var(--thm-field-fill);
  border-color: var(--thm-field-stroke);
  border-radius: var(--thm-control-radius);
  color: var(--crm-ink);
  box-shadow: var(--thm-field-inset);
}

html[data-theme] .form-control:focus {
  border-color: var(--thm-elevated-stroke);
  box-shadow: var(--thm-field-inset), 0 0 0 3px var(--thm-focus-ring);
}

/* The options of a native list box kept a grey of their own: 4.37:1 in
   Obsidian dark. They are the field's text. The SaaS settings show one
   <select multiple> without .form-control -- a selectpicker the plugin
   leaves native -- on the browser's own list background, so the list box
   itself is covered too, not only .form-control. */
html[data-theme] select.form-control option,
html[data-theme] select[multiple],
html[data-theme] select[multiple] option {
  color: var(--crm-ink);
}

/* A textarea is a control with the height of a card. A pill radius that suits
   a 36px field turns a five-line box into a lozenge, so it never rounds more
   than the surface it sits on. Every theme written before the control radius
   existed has elevated <= surface, so for them this changes nothing. */
html[data-theme] textarea.form-control {
  border-radius: min(var(--thm-control-radius), var(--thm-surface-radius));
}

/* --- input groups ---------------------------------------------------------
   Inside a group the corners belong to the group, not to each piece: only
   the outer ends take the control corner, and where two pieces meet the
   edge is square. Bootstrap squares those inner corners at (0,3,0); a theme
   rule that rounds every .form-control undoes that, and with Dynamic's pill
   corner "INV- | 000004" or a date and its calendar button read as two
   loose controls. Logical properties, so the same rules hold in RTL. These
   are (0,4,1). */
html[data-theme] .input-group > :is(.form-control, .input-group-addon):not(:first-child),
html[data-theme] .input-group > .input-group-btn:not(:first-child) > .btn {
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}

html[data-theme] .input-group > :is(.form-control, .input-group-addon):not(:last-child),
html[data-theme] .input-group > .input-group-btn:not(:last-child) > .btn {
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}

html[data-theme] .input-group > .input-group-addon:first-child,
html[data-theme] .input-group > .input-group-btn:first-child > .btn {
  border-start-start-radius: var(--thm-control-radius);
  border-end-start-radius: var(--thm-control-radius);
}

html[data-theme] .input-group > .input-group-addon:last-child,
html[data-theme] .input-group > .input-group-btn:last-child > .btn {
  border-start-end-radius: var(--thm-control-radius);
  border-end-end-radius: var(--thm-control-radius);
}

/* A bootstrap-select in a group ("Añadir artículo" and its +) sits inside a
   wrapper, so its button never matches the rules above and stayed round on
   both ends against a square-edged neighbour. Same joins, reached through
   the wrapper; .btn lifts them to (0,6,1), above the sales adapter's
   (0,5,2) select rule. */
html[data-theme] .input-group > :not(:first-child) .bootstrap-select > .btn.dropdown-toggle,
html[data-theme] .input-group > .bootstrap-select:not(:first-child) > .btn.dropdown-toggle {
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}

html[data-theme] .input-group > :not(:last-child) .bootstrap-select > .btn.dropdown-toggle,
html[data-theme] .input-group > .bootstrap-select:not(:last-child) > .btn.dropdown-toggle {
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}

/* "Tareas facturables": the select is itself the group's first
   .input-group-btn, and tailwind.css pins that button to 0.375rem with
   !important (.input-group-btn:first-child > select + .btn). Only the same
   weight answers it. */
html[data-theme] .input-group > .input-group-btn:first-child > select + .btn {
  border-radius: 0 !important;
  border-start-start-radius: var(--thm-control-radius) !important;
  border-end-start-radius: var(--thm-control-radius) !important;
}

html[data-theme] .input-group-addon {
  background: var(--thm-sunken-fill);
  border-color: var(--thm-field-stroke);
  color: var(--crm-ink-2);
}

/* The table search is a magnifier addon joined to the field
   (resources/css/table.css:307), with the join written for a 6px corner and
   the two halves painted in different fills. One control, one fill, one
   edge, the theme's corner. Its own selectors are (0,4,2); these (0,5,3). */
html[data-theme] div.dataTables_wrapper div.dataTables_filter .input-group > .input-group-addon,
html[data-theme] div.dataTables_wrapper div.dataTables_filter .input-group > input {
  background-color: var(--thm-field-fill);
  border-color: var(--thm-field-stroke);
  box-shadow: none;
}

html[data-theme] div.dataTables_wrapper div.dataTables_filter .input-group > .input-group-addon {
  border-radius: 0;
  border-start-start-radius: var(--thm-control-radius);
  border-end-start-radius: var(--thm-control-radius);
}

html[data-theme] div.dataTables_wrapper div.dataTables_filter .input-group > input {
  border-radius: 0;
  border-start-end-radius: var(--thm-control-radius);
  border-end-end-radius: var(--thm-control-radius);
}

/* --- level 2: elevated — soft blur, because these DO overlap content ------- */

html[data-theme] .dropdown-menu,
html[data-theme] .popover,
html[data-theme] .tooltip-inner {
  background: var(--thm-elevated-fill);
  -webkit-backdrop-filter: blur(var(--thm-elevated-blur)) saturate(var(--thm-sat));
          backdrop-filter: blur(var(--thm-elevated-blur)) saturate(var(--thm-sat));
  border: var(--thm-border-width) solid var(--thm-elevated-stroke);
  border-radius: var(--thm-elevated-radius);
  box-shadow: inset 0 1px 0 var(--thm-elevated-highlight), var(--thm-elevated-shadow);
}

/* Bootstrap ships the tooltip dark — white text on black — and draws its tip
   as a bordered triangle in eight directional variants, every one of them
   hardcoded. Repainting only the bubble above leaves white text on a light
   surface (invisible) with a black arrow pointing at it.

   The .in opacity has to go too: an ancestor below opacity 1 disables
   backdrop-filter on its descendants, so the bubble's blur was never running. */
html[data-theme] .tooltip.in {
  opacity: 1;
}

html[data-theme] .tooltip-inner {
  color: var(--crm-ink);
}

html[data-theme] .tooltip.top .tooltip-arrow,
html[data-theme] .tooltip.top-left .tooltip-arrow,
html[data-theme] .tooltip.top-right .tooltip-arrow {
  border-top-color: var(--thm-elevated-fill);
}

html[data-theme] .tooltip.bottom .tooltip-arrow,
html[data-theme] .tooltip.bottom-left .tooltip-arrow,
html[data-theme] .tooltip.bottom-right .tooltip-arrow {
  border-bottom-color: var(--thm-elevated-fill);
}

html[data-theme] .tooltip.right .tooltip-arrow {
  border-right-color: var(--thm-elevated-fill);
}

html[data-theme] .tooltip.left .tooltip-arrow {
  border-left-color: var(--thm-elevated-fill);
}

/* The popover tip is two stacked triangles: the outer one is the border, the
   ::after is the fill — pinned to #fff, which is a white wedge on any dark
   theme. Same defect as the tooltip arrow, so it is fixed alongside it. */
html[data-theme] .popover.top > .arrow {
  border-top-color: var(--thm-elevated-stroke);
}

html[data-theme] .popover.top > .arrow:after {
  border-top-color: var(--thm-elevated-fill);
}

html[data-theme] .popover.bottom > .arrow {
  border-bottom-color: var(--thm-elevated-stroke);
}

html[data-theme] .popover.bottom > .arrow:after {
  border-bottom-color: var(--thm-elevated-fill);
}

html[data-theme] .popover.right > .arrow {
  border-right-color: var(--thm-elevated-stroke);
}

html[data-theme] .popover.right > .arrow:after {
  border-right-color: var(--thm-elevated-fill);
}

html[data-theme] .popover.left > .arrow {
  border-left-color: var(--thm-elevated-stroke);
}

html[data-theme] .popover.left > .arrow:after {
  border-left-color: var(--thm-elevated-fill);
}

/* .btn-bottom-toolbar is position:fixed with content scrolling under it, so it
   is genuinely elevated — but it also has overflow:hidden, which clips what a
   backdrop-filter samples. Tint only. */
html[data-theme] .btn-bottom-toolbar {
  background: var(--thm-elevated-fill);
  border-top: var(--thm-border-width) solid var(--thm-elevated-stroke);
  box-shadow: inset 0 1px 0 var(--thm-elevated-highlight);
}

/* --- level 3: floating — real blur, there is a page behind these ----------- */

html[data-theme] .modal-content {
  background: var(--thm-floating-fill);
  -webkit-backdrop-filter: blur(var(--thm-floating-blur)) saturate(var(--thm-sat));
          backdrop-filter: blur(var(--thm-floating-blur)) saturate(var(--thm-sat));
  border: var(--thm-border-width) solid var(--thm-floating-stroke);
  border-radius: var(--thm-floating-radius);
  box-shadow: inset 0 1px 0 var(--thm-floating-highlight), var(--thm-floating-shadow);
}

/* El velo. resources/css/modal.css lo deja en neutral-700/70 y le suma
   opacity:.9 en .in — tan oscuro que cualquier superficie translúcida encima se
   lee como gris plano en vez de vidrio. La opacidad se anula y el alfa va en el
   color, para que el tema mande sobre las dos cosas. */
html[data-theme] .modal-backdrop,
html[data-theme] .modal-backdrop.in {
  background-color: var(--thm-scrim);
  opacity: 1;
}

html[data-theme] .modal-header,
html[data-theme] .modal-footer {
  background: transparent;
  border-color: var(--thm-surface-stroke);
}

/* ===========================================================================
   PHASE 2 — the rest of the admin
   =========================================================================== */

/* --- tabs -----------------------------------------------------------------
   resources/css/tabs.css still carries literal #ddd borders and a #fff active
   bottom edge, and Bootstrap's own .nav-tabs > li.active > a hardcodes a white
   background in the bundled vendor-admin.css we cannot edit. Both need the
   attribute-prefixed selector to be outranked. */

html[data-theme] .nav-tabs {
  border-bottom-color: var(--thm-surface-stroke);
}

html[data-theme] .nav-tabs > li > a {
  border-radius: var(--thm-elevated-radius) var(--thm-elevated-radius) 0 0;
  color: var(--crm-ink-2);
}

html[data-theme] .nav-tabs > li > a:hover {
  background: var(--thm-hover-fill);
  border-color: transparent;
}

html[data-theme] .nav-tabs > li.active > a,
html[data-theme] .nav-tabs > li.active > a:hover,
html[data-theme] .nav-tabs > li.active > a:focus {
  background: var(--thm-surface-fill);
  border-color: var(--thm-surface-stroke);
  border-bottom-color: transparent;
  color: var(--crm-ink);
}

html[data-theme] .horizontal-scrollable-tabs .horizontal-tabs .nav-tabs-horizontal {
  background: transparent;
}

/* The segmented variant (tabs.css:347, the spam filters) sits in a well of
   neutral-300/30 with a white chip for the active tab. The veil is light, so
   on a dark panel it turned grey and put the tabs at 3.7:1. */
html[data-theme] .nav-tabs-segmented,
html[data-theme] .horizontal-scrollable-tabs .horizontal-tabs .nav-tabs-horizontal.nav-tabs-segmented {
  background: var(--thm-sunken-fill);
  border-radius: var(--thm-control-radius);
}

html[data-theme] .nav-tabs-segmented > li > a {
  border-radius: var(--thm-control-radius);
}

html[data-theme] .nav-tabs-segmented > li.active > a,
html[data-theme] .nav-tabs-segmented > li.active > a:hover,
html[data-theme] .nav-tabs-segmented > li.active > a:focus,
html[data-theme] .nav-tabs-segmented .open > a {
  background: var(--thm-elevated-fill);
  border-color: transparent;
}

/* --- tables ---------------------------------------------------------------
   table.css already reads --crm-*, so the table itself follows the theme. What
   does not is the striping: an opaque zebra row inside a translucent panel
   reads as a solid block sitting on the glass. Tint it instead. */

html[data-theme] .table-striped > tbody > tr:nth-of-type(odd),
html[data-theme] table.dataTable.stripe tbody tr.odd {
  background-color: var(--thm-hover-fill);
}

html[data-theme] .table > tbody > tr:hover,
html[data-theme] table.dataTable tbody tr:hover {
  background-color: var(--thm-active-fill);
}

/* --- what a line may NOT be broken at --------------------------------------
   A date and a document number carry a hyphen, and a hyphen is a legal place
   to break a line: in a narrow column they came out as "2026-09-" / "20" and
   "INV-" / "000008". The cells that hold them are plain text or a single link
   to the document, so they are named here and kept on one line; the table
   widens instead, inside the scroller it already has. Every theme wraps them
   somewhere -- the lists from 1024px down -- and Neon's monospace, a fifth
   wider than Inter, wraps them on a full desktop. */
html[data-theme] :is(.dataTables_wrapper, .table-responsive) .table > tbody > tr > td:not(:has(*)):not(.description),
html[data-theme] .invoice-payments-table > tbody > tr > td:not(:has(*)),
html[data-theme] td > a[onclick^="init_"],
html[data-theme] td > a[href*="list_invoices/"],
html[data-theme] td > a[href*="list_estimates/"],
html[data-theme] td > a[href*="list_proposals/"],
html[data-theme] td > a[href*="list_credit_notes/"],
html[data-theme] td > a[href*="payments/payment/"],
html[data-theme] td > a[href*="expenses/list_expenses/"] {
  white-space: nowrap;
}

/* The same dates in the document preview, where the split view leaves the
   right-hand column ~280px: the label takes its own line so the date is not
   asked to share one. */
html[data-theme] :is(#invoice-preview, #estimate-preview, #credit-note-preview) p.no-mbot > span.bold {
  display: block;
}

/* Bootstrap's contextual rows: the modules list marks each active module
   tr.info, which tailwind.css paints sky-50 with sky-600 text. 3.8:1 in
   light; in dark the row stayed pale while its "Desactivar" link turned
   light, 1.0:1. Same status pairs as the labels. */
html[data-theme] .table > :is(thead, tbody, tfoot) > tr.info > :is(td, th),
html[data-theme] .table > :is(thead, tbody, tfoot) > tr > :is(td, th).info {
  background-color: var(--crm-brand-soft);
  color: color-mix(in srgb, var(--crm-brand) 70%, var(--crm-ink));
}

html[data-theme] .table > :is(thead, tbody, tfoot) > tr.success > :is(td, th),
html[data-theme] .table > :is(thead, tbody, tfoot) > tr > :is(td, th).success {
  background-color: var(--crm-ok-bg);
  color: color-mix(in srgb, var(--crm-ok) 70%, var(--crm-ink));
}

html[data-theme] .table > :is(thead, tbody, tfoot) > tr.warning > :is(td, th),
html[data-theme] .table > :is(thead, tbody, tfoot) > tr > :is(td, th).warning {
  background-color: var(--crm-warn-bg);
  color: color-mix(in srgb, var(--crm-warn) 70%, var(--crm-ink));
}

html[data-theme] .table > :is(thead, tbody, tfoot) > tr.danger > :is(td, th),
html[data-theme] .table > :is(thead, tbody, tfoot) > tr > :is(td, th).danger {
  background-color: var(--crm-bad-bg);
  color: color-mix(in srgb, var(--crm-bad) 70%, var(--crm-ink));
}

html[data-theme] .table > :is(thead, tbody, tfoot) > tr.active > :is(td, th),
html[data-theme] .table > :is(thead, tbody, tfoot) > tr > :is(td, th).active {
  background-color: var(--thm-active-fill);
}

/* The links in those rows ("Desactivar", "Actualizar") keep a grey of their
   own that is tuned for a plain row; on the status fill they read as the row
   does. */
html[data-theme] .table > tbody > tr:is(.info, .success, .warning, .danger) a:not(.btn) {
  color: inherit;
}

/* --- selects and date pickers: elevated, they overlay content ------------- */

html[data-theme] .bootstrap-select .dropdown-toggle {
  background: var(--thm-elevated-fill);
  border-color: var(--thm-surface-stroke);
  border-radius: var(--thm-control-radius);
}

/* A fit-width select (the currency switch on the invoice list) hangs its
   caret 6px OUTSIDE the button (resources/css/select.css:21) -- a detail
   drawn for a borderless control. The moment a theme gives the button an
   edge, the arrow sits beyond the box. Room is made inside instead -- and on
   the left too, where the same borderless idea zeroes the padding and left
   the currency symbol touching the edge. */
html[data-theme] .bootstrap-select.fit-width > .dropdown-toggle {
  padding-left: 10px;
  padding-right: 28px;
}

html[data-theme] .bootstrap-select.fit-width .dropdown-toggle .caret {
  right: 11px;
}

html[data-theme] .xdsoft_datetimepicker {
  background: var(--thm-elevated-fill);
  -webkit-backdrop-filter: blur(var(--thm-elevated-blur)) saturate(var(--thm-sat));
          backdrop-filter: blur(var(--thm-elevated-blur)) saturate(var(--thm-sat));
  border: var(--thm-border-width) solid var(--thm-elevated-stroke);
  border-radius: var(--thm-elevated-radius);
  box-shadow: var(--thm-elevated-shadow);
}

/* Its days (resources/css/datetimepicker.css). The cells are tw-bg-white on
   the elevated fill, the chosen day is Tailwind's blue in every theme, the
   hover is a fixed sky pair with !important, and in dark the CRM repaints the
   chosen day with the brand under literal white: 1.5:1 on Neon's pale cyan.
   The CRM's dark rules carry !important, so these do too. The chosen-day
   rules come after the hover ones, at the same weight, so hovering the
   chosen day keeps it chosen. */
html[data-theme] .xdsoft_datetimepicker .xdsoft_calendar td,
html[data-theme] .xdsoft_datetimepicker .xdsoft_calendar th {
  background-color: transparent;
}

/* The weekday initials: a fixed neutral-500, 4.2:1 on Dynamic's menu. */
html[data-theme] .xdsoft_datetimepicker .xdsoft_calendar th {
  color: var(--crm-ink-3);
}

html[data-theme] .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today {
  color: color-mix(in srgb, var(--crm-brand) 70%, var(--crm-ink));
}

html[data-theme] .xdsoft_datetimepicker .xdsoft_calendar td:hover,
html[data-theme] .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box > div > div:hover {
  background-color: var(--thm-active-fill) !important;
  color: var(--crm-ink) !important;
}

html[data-theme] .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default,
html[data-theme] .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current,
html[data-theme] .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box > div > div.xdsoft_current,
html[data-theme] .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current {
  background-color: var(--crm-brand) !important;
  color: var(--thm-on-brand) !important;
  box-shadow: none !important;
}

/* --- surfaces style.css paints with fixed colours -------------------------
   #newsfeed (#f4f4f5) and .system-popup (#f9fafb) are full-screen overlays, so
   they are floating and must fully cover what is behind them: opaque, like the
   setup menu. #toplink/#botlink are small fixed affordances. */

html[data-theme] #newsfeed,
html[data-theme] .system-popup {
  background: var(--crm-canvas);
}

html[data-theme] .system-popup .popup-wrapper,
html[data-theme] .system-popup .popup-content {
  background: var(--crm-surface);
  border-radius: var(--thm-floating-radius);
}

html[data-theme] #toplink,
html[data-theme] #botlink {
  background: var(--thm-elevated-fill);
  border: var(--thm-border-width) solid var(--thm-elevated-stroke);
  box-shadow: var(--thm-elevated-shadow);
}

/* --- the sidebar profile card ---------------------------------------------
   aside.php picks literal Tailwind classes from is_admin_sidebar_background_light()
   (modules/theme_style/helpers/theme_style_helper.php:502), a value that no
   longer paints anything once a theme is active. Left alone it renders an
   opaque white pill on the glass sidebar. */

html[data-theme] aside#menu .tw-bg-white {
  background-color: var(--thm-sunken-fill) !important;
  border-color: var(--thm-surface-stroke) !important;
}

/* The card is sunken, a step darker than the surface the quiet ink is
   measured against: the e-mail under the name fell to 4.4:1. Secondary ink
   holds on it. */
html[data-theme] aside#menu .tw-bg-white .tw-text-neutral-500 {
  color: var(--crm-ink-2);
}

/* --- TinyMCE chrome -------------------------------------------------------
   The editor's iframe body is set from JS (assets/js/main.js:3899) and reads
   the opaque --crm-* tokens, so it needs nothing here. Only the toolbar and
   border around it are ours. */

html[data-theme] .tox-tinymce {
  border-color: var(--thm-surface-stroke) !important;
  border-radius: var(--thm-elevated-radius) !important;
}

/* ===========================================================================
   PHASE 2b — the long tail
   ---------------------------------------------------------------------------
   Surfaces that style.css paints with fixed colours. None of these are exotic:
   they are the task modal, the kanban board, the column chooser, the comment
   threads. Left alone each one is an opaque rectangle on the glass, and a
   theme that covers the shell but not these reads as unfinished the moment
   someone opens a task.

   Found with check-coverage.php, which lists every literal-background selector
   in the CRM's own CSS that no rule of ours mentions.
   =========================================================================== */

/* --- containers: surface level, no blur ---------------------------------- */

html[data-theme] .task-single-col-left,
html[data-theme] .task-single-col-right,
html[data-theme] .activity-feed,
html[data-theme] .proposal-comments,
html[data-theme] .jquery-comments,
html[data-theme] .sub-items,
html[data-theme] .s_table.table-responsive,
html[data-theme] .email-template-heading,
html[data-theme] .lead-info-heading,
html[data-theme] table.items {
  background: var(--thm-surface-fill);
  border-color: var(--thm-surface-stroke);
}

/* The row where a new line item is typed (style.css:1845) is a fixed
   #f9fafb, in dark too. Under Liquid Glass's translucent cells it showed
   through as a pale band with light text on it: 1.6:1 on "Sin impuestos".
   It is an entry area, so it is sunken; its cells let that show. */
html[data-theme] .table.items .main {
  background-color: var(--thm-sunken-fill);
  border-color: var(--thm-surface-stroke);
}

html[data-theme] .table.items tr.main > td {
  background-color: transparent;
}

/* Quiet recessed areas: they must stay visible against the surface, so they
   use the sunken fill rather than a hover tint. */
html[data-theme] .kan-ban-content,
html[data-theme] .kan-ban-expand-top,
html[data-theme] .screen-options-area,
html[data-theme] .popover-title,
html[data-theme] .task-info-inline-input-edit,
html[data-theme] .dropzone,
html[data-theme] .highlight-bg,
html[data-theme] .task-comment.highlight-bg {
  background: var(--thm-sunken-fill);
  border-color: var(--thm-surface-stroke);
}

/* The drop zone's hint ("Dejar caer archivo aquí para subir"): the portal
   writes it #03a9f4 (assets/themes/perfex/css/style.css:1233), a light blue
   at 2.2:1 on every light palette and blue whatever the brand. */
html[data-theme] .dropzone .dz-message {
  color: var(--crm-ink-2);
}

html[data-theme] .screen-options-btn {
  background: var(--thm-sunken-fill);
  border-color: var(--thm-surface-stroke);
  color: var(--crm-ink-2);
}

/* --- overlays: elevated, they sit over content --------------------------- */

html[data-theme] .notification-wrapper,
html[data-theme] .dropdown-submenu > .dropdown-menu,
html[data-theme] .lead-modal,
html[data-theme] .task-modal-single .modal-content {
  background: var(--thm-elevated-fill);
  border-color: var(--thm-elevated-stroke);
}

/* TinyMCE's own dialog pins #fff with !important, so specificity alone loses:
   in a dark palette it opens as a white window in the middle of the screen. */
html[data-theme] .mce-window-body,
html[data-theme] .mce-window-head {
  background: var(--thm-elevated-fill) !important;
  border-color: var(--thm-elevated-stroke);
}

/* The task modal's BODY carries its own #f8fafc, so styling .modal-content was
   not enough: an opaque slab sat inside the glass card and the whole popup read
   as flat. The body has no fill of its own to contribute — the card behind it
   already provides one. */
html[data-theme] .task-modal-single .modal-body {
  background: transparent;
}

/* --- small accents -------------------------------------------------------- */

html[data-theme] span.mention {
  background: var(--crm-brand-soft);
  color: var(--crm-brand);
}

/* ===========================================================================
   COLOUR THE CRM HARDCODES
   ---------------------------------------------------------------------------
   Everything below was found by auditing text contrast and fixed Tailwind
   blues on every main screen, in all ten themes, light and dark. None of it
   is new: the CRM paints these with Tailwind's own scales, blue whatever the
   brand, and a theme only makes it visible -- in dark most of it could not be
   read at all.

   Tailwind compiles its @layer blocks away, so this is plain specificity:
   (0,2,1) here against the (0,1,0) classes in labels.css, text.css and
   alerts.css.

   Text in a status colour -- labels, text-success and friends, alerts -- is
   that colour pulled 30% toward the ink. Plain status on its soft fill fell
   below AA in several themes (Liquid Glass dark brand 3.90:1, Swiss light
   4.32:1, the CRM's own greens and ambers ~3:1); the mix keeps every pair,
   on the surface and on the soft fill, at 4.66:1 or better in all ten, and
   no theme's brand has to change. Info has no token of its own and reads as
   brand, as it does in the sales adapter.
   =========================================================================== */

/* --- labels ("Periódico", statuses) -------------------------------------- */

html[data-theme] .label-default {
  background-color: var(--thm-sunken-fill);
  color: var(--crm-ink-2);
  --tw-ring-color: var(--thm-surface-stroke);
}

html[data-theme] .label-primary,
html[data-theme] .label-info {
  background-color: var(--crm-brand-soft);
  color: color-mix(in srgb, var(--crm-brand) 70%, var(--crm-ink));
  --tw-ring-color: transparent;
}

html[data-theme] .label-success {
  background-color: var(--crm-ok-bg);
  color: color-mix(in srgb, var(--crm-ok) 70%, var(--crm-ink));
  --tw-ring-color: transparent;
}

html[data-theme] .label-warning {
  background-color: var(--crm-warn-bg);
  color: color-mix(in srgb, var(--crm-warn) 70%, var(--crm-ink));
  --tw-ring-color: transparent;
}

html[data-theme] .label-danger {
  background-color: var(--crm-bad-bg);
  color: color-mix(in srgb, var(--crm-bad) 70%, var(--crm-ink));
  --tw-ring-color: transparent;
}

html[data-theme] a.label:is(.label-default, .label-primary, .label-info, .label-success,
                            .label-warning, .label-danger):is(:hover, :focus) {
  filter: brightness(.96);
}

/* --- text utilities (resources/css/text.css) ----------------------------- */

html[data-theme] .text-primary,
html[data-theme] .text-info {
  color: color-mix(in srgb, var(--crm-brand) 70%, var(--crm-ink));
}

html[data-theme] .text-success {
  color: color-mix(in srgb, var(--crm-ok) 70%, var(--crm-ink));
}

html[data-theme] .text-warning {
  color: color-mix(in srgb, var(--crm-warn) 70%, var(--crm-ink));
}

html[data-theme] .text-danger {
  color: color-mix(in srgb, var(--crm-bad) 70%, var(--crm-ink));
}

html[data-theme] .text-muted {
  color: var(--crm-ink-3);
}

html[data-theme] a.text-muted:is(:hover, :focus) {
  color: var(--crm-ink-2);
}

/* Muted text inside a control (the "CRC" beside ₡) sits on the field fill,
   not the surface the quiet ink is measured against: 4.4:1 in Obsidian. */
html[data-theme] .bootstrap-select .filter-option .text-muted {
  color: var(--crm-ink-2);
}

/* Bootstrap's near-black. On any dark palette it is text you cannot see:
   "Total de clientes" measured 1.5:1. */
html[data-theme] .text-dark {
  color: var(--crm-ink);
}

/* --- links -----------------------------------------------------------------
   tailwind.css gives every <a> Tailwind's primary-700: blue in a violet or
   red theme, and 1.7:1 on Dynamic's dark surface. :where() keeps this at
   element weight, so it only replaces that base rule -- it beats it on
   source order -- and every class that colours a link itself (.text-muted,
   .tw-text-*, buttons, menus, tabs) still wins. */
:where(html[data-theme]) a {
  color: color-mix(in srgb, var(--crm-brand) 70%, var(--crm-ink));
}

:where(html[data-theme]) a:is(:hover, :focus) {
  color: var(--crm-ink);
}

/* In dark the CRM colours links itself -- theme-tokens.css,
   html[data-theme="dark"] a and a:focus, (0,1,2) -- with the plain brand,
   which outranks the element-weight rule above. With Liquid Glass's darker
   brand that put "Facturas recurrentes" at 3.7:1. Same selectors, loaded
   later, with the mix. */
html[data-theme="dark"] a,
html[data-theme="dark"] a:focus {
  color: color-mix(in srgb, var(--crm-brand) 70%, var(--crm-ink));
}

/* --- alerts (resources/css/alerts.css) -------------------------------------
   The heading of each alert kept a dark Tailwind shade after the dark
   palette darkened the alert itself: "Cron Job Warning" sat at 1.0:1. Fill,
   edge and text now come from one status pair, and the heading and links
   inherit instead of carrying a shade of their own. */
html[data-theme] .alert-success {
  background-color: var(--crm-ok-bg);
  border-color: var(--crm-ok);
  color: color-mix(in srgb, var(--crm-ok) 70%, var(--crm-ink));
}

html[data-theme] .alert-info {
  background-color: var(--crm-brand-soft);
  border-color: var(--crm-brand);
  color: color-mix(in srgb, var(--crm-brand) 70%, var(--crm-ink));
}

html[data-theme] .alert-warning {
  background-color: var(--crm-warn-bg);
  border-color: var(--crm-warn);
  color: color-mix(in srgb, var(--crm-warn) 70%, var(--crm-ink));
}

html[data-theme] .alert-danger {
  background-color: var(--crm-bad-bg);
  border-color: var(--crm-bad);
  color: color-mix(in srgb, var(--crm-bad) 70%, var(--crm-ink));
}

/* A plain link inside an alert ("página de configuración") would take the
   link colour, which is tuned for the surface, not for a warning fill: 3.7:1
   in dark. It takes the alert's text and keeps the underline to read as a
   link. */
html[data-theme] .alert :is(h4, .alert-link, a:not(.btn)) {
  color: inherit;
}

html[data-theme] .alert a:not(.btn) {
  text-decoration: underline !important;
}

html[data-theme] .alert hr {
  border-top-color: currentColor;
  opacity: .25;
}

/* --- quiet text with a fixed grey -------------------------------------------
   The table footer ("Mostrando desde 1 hasta…", table.css:339) and an empty
   select's hint ("Nada seleccionado", select.css:68, #a1b4cc) both sat below
   AA: the footer at 2.9:1 in dark, the hint at 2.2:1 in light. */
html[data-theme] div.dataTables_wrapper div.dataTables_info,
html[data-theme] .bootstrap-select .bs-placeholder .filter-option {
  color: var(--crm-ink-3);
}

/* Help text under a field, and the heading of a dropdown section ("Creación
   rápida"), are Bootstrap's fixed greys (#737373, #777): 3.0:1 on Liquid
   Glass, 4.2:1 on Dynamic's menu, worse on every dark palette. Both are the
   quietest ink. */
html[data-theme] .help-block,
html[data-theme] .dropdown-menu > .dropdown-header {
  color: var(--crm-ink-3);
}

/* --- on/off switch (form.css:511): on is the brand, not Tailwind blue ---- */
html[data-theme] .onoffswitch-checkbox:checked + .onoffswitch-label,
html[data-theme] .onoffswitch-checkbox:checked + .onoffswitch-label::before {
  border-color: var(--crm-brand);
}

html[data-theme] .onoffswitch-checkbox:checked + .onoffswitch-label {
  background-color: var(--crm-brand);
}

/* --- calendar (resources/css/fullcalendar.css) ------------------------------
   Every rule there carries !important, so these do too. In dark the weekday
   headings were 1.4:1 and the view buttons 1.5:1; today's date was a fixed
   blue disc in every theme. */
html[data-theme] .fc .fc-col-header-cell-cushion {
  color: var(--crm-ink-2) !important;
}

html[data-theme] .fc .fc-daygrid-day-number,
html[data-theme] .fc .fc-timegrid-slot-label-cushion,
html[data-theme] .fc .fc-more-link {
  color: var(--crm-ink-3) !important;
}

html[data-theme] .fc .fc-day.fc-day-today .fc-daygrid-day-number {
  background-color: var(--crm-brand) !important;
  color: var(--thm-on-brand) !important;
}

html[data-theme] .fc .fc-button-primary {
  background-color: var(--thm-elevated-fill) !important;
  border-color: var(--thm-surface-stroke) !important;
  color: var(--crm-ink-2) !important;
}

html[data-theme] .fc .fc-button-primary:is(:hover, :focus) {
  background-color: var(--thm-hover-fill) !important;
  color: var(--crm-ink) !important;
}

html[data-theme] .fc .fc-button-primary:is(:active, .fc-button-active) {
  background-color: var(--crm-brand) !important;
  border-color: var(--crm-brand) !important;
  color: var(--thm-on-brand) !important;
}

html[data-theme] .fc .fc-popover {
  background-color: var(--thm-elevated-fill) !important;
}

html[data-theme] .fc {
  --fc-highlight-color: var(--crm-brand-soft);
}

/* --- notifications dropdown (style.css:4885) --------------------------------
   An unread notification is a fixed #eff6ff row with blue text. In dark the
   title turned light while the row stayed pale: 1.02:1, a list of blanks
   with only the dates readable. The row is the brand's soft fill, the title
   is ink, and the date carries the brand. */
html[data-theme] .notification-wrapper .unread-notification {
  background: var(--crm-brand-soft);
  color: var(--crm-ink);
}

html[data-theme] .notification-wrapper .unread-notification:hover {
  background: color-mix(in srgb, var(--crm-brand-soft) 88%, var(--crm-brand));
  color: var(--crm-ink);
}

html[data-theme] .notification-wrapper .unread-notification :is(.notification-title, .notification-box) {
  color: var(--crm-ink);
}

html[data-theme] .notification-wrapper .notification-date {
  color: var(--crm-ink-3);
}

html[data-theme] .notification-wrapper .unread-notification .notification-date {
  color: color-mix(in srgb, var(--crm-brand) 70%, var(--crm-ink));
}

/* --- header links ("Ajustes" and the icon links beside it) ----------------
   A fixed #6b7280, below AA on a dark header. They are secondary text, so
   they take the theme's secondary ink -- which check-contrast.php already
   holds to AA on every surface. */
html[data-theme] #header .navbar-nav > li > a {
  color: var(--crm-ink-2);
}

html[data-theme] #header .navbar-nav > li > a:hover,
html[data-theme] #header .navbar-nav > li > a:focus {
  color: var(--crm-ink);
}

/* --- colours written into the markup ---------------------------------------
   The company name on an invoice, payment or statement is <b style=
   "color:black"> (application/helpers/sales_helper.php:533), and an item's
   long description is <span style="color:#424242"> (App_items_table.php:88).
   Both vanish on a dark palette (1.0:1 and 1.4:1); an inline style only
   yields to !important. The PDF has its own stylesheet and keeps them. */
html[data-theme] .company-name-formatted {
  color: var(--crm-ink) !important;
}

html[data-theme] .table.items td.description > span[style*="color"] {
  color: var(--crm-ink-2) !important;
}

/* --- the label of a field that is off ---------------------------------------
   form.css greys it with a fixed neutral-500 (#6b7280): 4.4:1 on Dynamic's
   tinted surface. It is the quietest ink, which is held to AA. */
html[data-theme] .form-group:has(.bootstrap-select.disabled) label,
html[data-theme] .form-group:has(.form-control[disabled]) label {
  color: var(--crm-ink-3);
}

/* ...but that :has() reaches every label in the group, so "Infinito", the
   live checkbox beside the cycles field it switches off, was dimmed to .6 as
   though it were off too: 3.9:1 in every light theme. */
html[data-theme] .form-group:has(.form-control[disabled]) .checkbox input:not(:disabled) + label {
  color: var(--crm-ink-2);
  opacity: 1;
}

/* --- the dashboard's payments widget on a phone ------------------------------
   Its title and its three controls (currency, full report, weekly) share one
   line that never wraps (views/admin/dashboard/widgets/payments_chart.php):
   160px past the edge of a phone, the one thing that scrolled the dashboard
   sideways. Not a colour, but a core view cannot be edited and this is the
   layer every screen already loads. */
html[data-theme] #payments .panel-body > .tw-justify-between,
html[data-theme] #payments .panel-body > .tw-justify-between > .tw-divide-x {
  flex-wrap: wrap;
  row-gap: 8px;
}

/* The dashboard's stat cards (top_stats.php, and perfex_saas's overview and
   quota tiles) put a label and a figure at the two ends of a flex row with
   no gap. A truncated label ran into its figure ("Clientes potencial...0 / 0"),
   and perfex_saas spaces its labels with mr-2, a Bootstrap 4 class this
   Bootstrap 3 does not have: "Ingresos₡28,250.00", the amount spilling past
   the card, with or without a theme. A gap; and where the label cannot be
   cut short, the figure drops below it once the label has shrunk as far as
   its longest word. */
html[data-theme] .top_stats_wrapper > .tw-justify-between {
  column-gap: 8px;
}

html[data-theme] .top_stats_wrapper > .tw-justify-between:not(:has(.tw-truncate)) {
  flex-wrap: wrap;
}

html[data-theme] .top_stats_wrapper > .tw-justify-between:not(:has(.tw-truncate)) > :first-child {
  flex: 1 1 0;
  min-width: min-content;
}

/* The finance summary (finance_overview.php) lifts each percentage 25px
   (style.css:3634) onto the line of its status label, and nothing keeps the
   two apart: "1 Pagada parcialmente9.09%". The label leaves the percentage
   its room and wraps before reaching it. */
html[data-theme] .home-summary .text-stats-wrapper > a {
  padding-right: 4.5em;
}

/* Same on the invoice list's totals (invoices_total_template.php): three
   chips that may not shrink, on a line that may not wrap. They fit in Inter;
   in Neon's monospace they ran 36px past a tablet's edge. */
html[data-theme] .md\:tw-flex:has(> .tw-shrink-0.tw-border-neutral-300\/80),
html[data-theme] ._buttons > .md\:tw-flex {
  flex-wrap: wrap;
  row-gap: 8px;
}

/* A customer's profile puts its tab list and the tab's content side by side
   in a flex row, and a flex child will not shrink below its content: the
   invoices table pushed the page 70px sideways at 1280px, with or without a
   theme. min-width lets the column give way, and the table scrolls inside
   its own .table-responsive as it should. */
html[data-theme] .customer_profile .md\:tw-flex > .tw-w-full {
  min-width: 0;
}

/* The currency switch in that row is a bootstrap-select at its stock 220px
   for a two-letter symbol; on a phone it alone was half the row. */
@media (max-width: 767px) {
  html[data-theme] #payments .panel-body > .tw-justify-between .bootstrap-select {
    width: auto !important;
  }
}

/* --- the search hint of a remote select ("Comienza a escribir...") ----------
   neutral-500 on neutral-100, 4.4:1. */
html[data-theme] .bootstrap-select .status {
  background: var(--thm-sunken-fill);
  color: var(--crm-ink-2);
}

/* Two accents hardcoded to a blue that belongs to no theme: the drag-hover
   handle and the comment highlight. Both carry !important. */
html[data-theme] .dd-hover > .dd-handle,
html[data-theme] .jquery-comments .highlight-background {
  background: var(--crm-brand) !important;
  color: var(--thm-on-brand);
}

/* style.css paints these on the .panel-body INSIDE the item, with !important
   on both the fill and the border. Tinting the <li> alone leaves that opaque
   card sitting on top of the tint, so the override has to land on the same
   element and carry the same weight. */
html[data-theme] li.task.current-user-task,
html[data-theme] li.lead-kan-ban.current-user-lead {
  background: var(--crm-brand-soft);
}

html[data-theme] li.task.current-user-task .panel-body,
html[data-theme] li.lead-kan-ban.current-user-lead .panel-body {
  background: var(--crm-brand-soft) !important;
  border-color: var(--thm-surface-stroke) !important;
}

html[data-theme] li.task.overdue-task {
  background: var(--crm-bad-bg);
}

html[data-theme] li.task.overdue-task .panel-body {
  background: var(--crm-bad-bg) !important;
  border-color: var(--thm-surface-stroke) !important;
}

/* Nestable drag-and-drop lists (menu setup, KB article ordering). The rows
   carry !important, and .sub-items nests a second one that is whiter still. */
html[data-theme] .dd-handle,
html[data-theme] .dd3-content,
html[data-theme] .dd3-handle,
html[data-theme] .sub-items .dd3-content {
  background: var(--thm-sunken-fill) !important;
  border-color: var(--thm-surface-stroke);
  color: var(--crm-ink);
}

html[data-theme] .dd-placeholder,
html[data-theme] .dd-hover > .dd-handle {
  background: var(--thm-active-fill);
  border-color: var(--thm-elevated-stroke);
}

html[data-theme] .mobile-navbar {
  background: var(--thm-surface-fill);
  border-color: var(--thm-surface-stroke);
}

/* The phone's profile menu (Mi perfil, Cerrar sesión...). style.css:500 writes
   its links as #334155 !important with #e2e8f0 rules between them: in dark,
   slate on a dark panel at 1.4:1 behind bright white seams. */
html[data-theme] .mobile-navbar .navbar-nav {
  border-bottom-color: var(--thm-surface-stroke);
}

html[data-theme] .mobile-navbar .navbar-nav > li > a {
  color: var(--crm-ink-2) !important;
  border-bottom-color: var(--thm-surface-stroke);
}

html[data-theme] .mobile-navbar .navbar-nav > li > a:is(:hover, :focus) {
  background: var(--thm-hover-fill);
  color: var(--crm-ink) !important;
}

html[data-theme] .dt-loader,
html[data-theme] .setup-menu-loading {
  background: var(--thm-surface-fill);
}

/* Remaining surfaces from check-coverage.php that are actually reachable. */

html[data-theme] .nestable2,
html[data-theme] textarea[name="checklist-description"],
html[data-theme] .question.form-group {
  background: var(--thm-surface-fill);
  border-color: var(--thm-surface-stroke);
}

/* The disabled checklist textarea keeps its own near-black colour, which the
   fill above cannot help with: on a dark palette it is text you cannot read.
   Its hover pair paints a fixed light grey on the row as well. */
html[data-theme] textarea[name="checklist-description"]:disabled {
  background: var(--thm-surface-fill);
  color: var(--crm-ink);
}

html[data-theme] .checklist:hover,
html[data-theme] .checklist:hover textarea[name="checklist-description"]:disabled {
  background: var(--thm-hover-fill);
}

html[data-theme] .project_progress_slider_horizontal,
html[data-theme] .project_progress_slider_horizontal.ui-widget.ui-widget-content {
  background: var(--thm-sunken-fill);
  border-color: var(--thm-surface-stroke);
}

/* Deliberately NOT covered, and why:
   - .gpicker is Google's own picker chrome, rendered in its iframe.
   - .mce-* / .tox-* / .tinymce-mobile-* are TinyMCE internals. The editor's
     content body is set from JS off the opaque --crm-* tokens
     (assets/js/main.js:3899); restyling its dialogs from here fights the
     editor's own skin and breaks on upgrade.
   - body.show-sidebar is a layout state, not a surface. */

/* ===========================================================================
   PHASE 3 — the client portal
   ---------------------------------------------------------------------------
   assets/themes/perfex/css/style.css is 1818 lines with ZERO references to
   --crm-*: 34 surfaces painted with literal colour. Nothing here comes for
   free the way the admin did.

   We load after it (our handles hang off theme-css), so ties go to us, and the
   html[data-theme] prefix wins outright where it does not. The file itself is
   not edited: a CRM upgrade overwrites it.
   =========================================================================== */

html[data-theme] body.customers {
  background-color: var(--crm-canvas);
  background-image: var(--thm-backdrop);
  background-attachment: fixed;
}

html[data-theme] body.customers .navbar-default,
html[data-theme] body.customers .bgwhite {
  background: var(--thm-surface-fill);
  border-color: var(--thm-surface-stroke);
  box-shadow: inset 0 1px 0 var(--thm-surface-highlight);
}

html[data-theme] body.customers .navbar-default .navbar-nav > li > a {
  color: var(--crm-ink-2);
  border-radius: var(--thm-control-radius);
}

html[data-theme] body.customers .navbar-default .navbar-nav > .active > a,
html[data-theme] body.customers .navbar-default .navbar-nav > .active > a:hover,
html[data-theme] body.customers .navbar-default .navbar-nav > li > a:hover,
html[data-theme] body.customers .navbar-default .navbar-nav > li > a:focus {
  background: var(--thm-active-fill);
  color: var(--crm-ink);
}

html[data-theme] body.customers .submenu,
html[data-theme] body.customers .customer-top-submenu {
  background: var(--thm-surface-fill);
  border-color: var(--thm-surface-stroke);
}

html[data-theme] body.customers footer,
html[data-theme] body.customers .table.items thead,
html[data-theme] body.customers .project-info-bg,
html[data-theme] body.customers #task-comments {
  background: var(--thm-sunken-fill);
  border-color: var(--thm-surface-stroke);
}

/* The "your reply" highlight is a warm tint carrying meaning, so it maps to the
   warning token rather than to a neutral surface. */
html[data-theme] body.customers .client-reply .panel-body {
  background: var(--crm-warn-bg);
}

/* The company name, shown when there is no logo. The portal pins it to a
   slate (style.css:588, #1e293b) that vanishes on every dark palette.
   check-coverage.php looks for literal BACKGROUNDS, which is why it never
   flagged a literal text colour like this one. */
html[data-theme] body.customers .navbar-default .navbar-brand.logo-text,
html[data-theme] body.customers .navbar-default .navbar-brand.logo-text:hover,
html[data-theme] body.customers .navbar-default .navbar-brand.logo-text:focus {
  color: var(--crm-ink);
}

/* The portal's "Login" button is Tailwind blue with !important
   (assets/themes/perfex/css/style.css), in every theme and on every
   studio's login page. It is the brand's call to action. */
html[data-theme] body.customers .navbar-default .navbar-nav > li.customers-nav-item-login > a {
  background-color: var(--crm-brand) !important;
  color: var(--thm-on-brand) !important;
}

html[data-theme] body.customers .navbar-default .navbar-toggle .icon-bar {
  background: var(--crm-ink-2);
}

/* On a phone the menu folds into a column, and Bootstrap pulls the list 15px
   out to the screen's edge so plain text links line up with the logo. Ours
   are pills now: the active one's fill ran into the edge, square, while the
   Login pill beside it kept the portal's own 15px inset. Every pill takes
   that inset. */
@media (max-width: 767px) {
  html[data-theme] body.customers .navbar-default .navbar-collapse .navbar-nav > li > a {
    margin: 0 15px 4px;
  }
}

/* The portal's layout already wraps every view in .container > .row
   (themes/perfex/index.php), and the home view opens a .row of its own
   inside it: two -15px gutters stacked, 12px past a tablet's edge with or
   without a theme. The inner one gives its gutter back. */
html[data-theme] body.customers #content > .container > .row > .row {
  margin-left: 0;
  margin-right: 0;
}

/* The calendar's toolbar (arrows, month, Mes/Semana/Día) is one row that
   never wraps in the portal -- the admin's style.css lets it wrap, the
   portal's does not -- and ran 23px past a phone's edge. */
html[data-theme] body.customers .fc .fc-header-toolbar {
  flex-wrap: wrap;
  gap: 8px;
}

/* The knowledge base's search banner is a fixed #f9fafb (style.css). On a
   dark palette its heading turned light while the banner stayed pale: 1.0:1,
   a blank box above the search field. */
html[data-theme] body.customers .kb-search-jumbotron {
  background: var(--thm-surface-fill);
  border-bottom: var(--thm-border-width) solid var(--thm-surface-stroke);
}

/* The portal's own semantic fills. Literal brand colours that predate the
   token layer; repointing them keeps status meaning while following the theme. */
html[data-theme] body.customers .success-bg { background: var(--crm-ok); }
html[data-theme] body.customers .warning-bg { background: var(--crm-warn); }
html[data-theme] body.customers .danger-bg  { background: var(--crm-bad); }
html[data-theme] body.customers .primary-bg,
html[data-theme] body.customers .info-bg    { background: var(--crm-brand); }

/* The single-document views (invoice, estimate, proposal) are driven by the
   --s-* adapter, which already covers them: their body classes are in the
   adapter's :is() list. Nothing extra needed here. */

/* --- the primary button ---------------------------------------------------
   resources/css/buttons.css:201 makes it a charcoal (@apply tw-bg-neutral-800).
   The approved design puts the brand colour here instead, which is squarely a
   theme's business. Compiled @apply rules are plain class selectors, so the
   attribute prefix outranks them. */

html[data-theme] .btn-primary {
  background: var(--crm-brand);
  border-color: var(--crm-brand);
  color: var(--thm-on-brand);
  box-shadow: inset 0 1px 0 var(--thm-surface-highlight);
}

html[data-theme] .btn-primary:hover,
html[data-theme] .btn-primary:focus,
html[data-theme] .btn-primary.focus {
  background: var(--crm-brand);
  border-color: var(--crm-brand);
  color: var(--thm-on-brand);
  filter: brightness(1.08);
}

html[data-theme] .btn-primary:active,
html[data-theme] .btn-primary.active {
  filter: brightness(.94);
}

/* --- the status buttons -----------------------------------------------------
   buttons.css paints them with Tailwind's scales under white text. White on
   info-600 is 4.1:1 ("Test" in the e-mail settings), and a button written as
   a link lost its white in dark altogether: the dark link rule, (0,1,2),
   outranks .btn-danger, (0,1,0), and put "Limpiar el log" at 1.5:1 on red.

   They take the theme's status colours instead. The text is the theme's own
   surface: light on a light palette's deep status colour, dark on a dark
   palette's bright one -- 4.8:1 or better on all ten, where white on a dark
   palette's bright green is 1.5:1. Info reads as brand, as everywhere. */
html[data-theme] .btn-info,
html[data-theme] .btn-info:is(:hover, :focus, :active, .active) {
  background: var(--crm-brand);
  border-color: var(--crm-brand);
  color: var(--thm-on-brand);
}

html[data-theme] .btn-success,
html[data-theme] .btn-success:is(:hover, :focus, :active, .active) {
  background: var(--crm-ok);
  border-color: var(--crm-ok);
  color: var(--crm-surface);
}

html[data-theme] .btn-warning,
html[data-theme] .btn-warning:is(:hover, :focus, :active, .active) {
  background: var(--crm-warn);
  border-color: var(--crm-warn);
  color: var(--crm-surface);
}

html[data-theme] .btn-danger,
html[data-theme] .btn-danger:is(:hover, :focus, :active, .active) {
  background: var(--crm-bad);
  border-color: var(--crm-bad);
  color: var(--crm-surface);
}

html[data-theme] :is(.btn-info, .btn-success, .btn-warning, .btn-danger):is(:hover, :focus) {
  filter: brightness(1.08);
}

html[data-theme] :is(.btn-info, .btn-success, .btn-warning, .btn-danger):is(:active, .active) {
  filter: brightness(.94);
}

/* The same Tailwind pairs as plain utilities (resources/css/background.css):
   perfex_saas's "core" badge is white on green-500, 2.3:1. */
html[data-theme] :is(.bg-primary, .bg-info) {
  background-color: var(--crm-brand);
  color: var(--thm-on-brand);
}

html[data-theme] .bg-success {
  background-color: var(--crm-ok);
  color: var(--crm-surface);
}

html[data-theme] .bg-warning {
  background-color: var(--crm-warn);
  color: var(--crm-surface);
}

html[data-theme] .bg-danger {
  background-color: var(--crm-bad);
  color: var(--crm-surface);
}

/* Form controls need a visible edge to read as inputs; the surface stroke is
   deliberately faint and disappears on a filled control. */
html[data-theme] .form-control,
body.login_admin .form-control {
  border-color: var(--thm-elevated-stroke);
}
