/* ---------------------------------------------------------------------------
   Design tokens. Loaded after the Sneat theme, so it is the last word.
   See .claude/skills/saas-ui/SKILL.md for the rules that go with these names.

   The brand and semantic values below are the theme's own, not a second set
   sitting beside it. That is the whole point: one palette, taken from whoever
   already paints the most surface. A deployment that needs a different look
   overrides ONLY the brand block — see brand-uautoflow.css on the uautoflow
   branch, which restates those few lines and nothing else.
   --------------------------------------------------------------------------- */
:root {
  /* Brand — the only block a deployment overrides.
     This started as #667eea/#764ba2, lifted from a decorative gradient in the
     client views. Wrong source: the theme's primary is #4063F4 and it paints
     that 178 times, so keying the brand off a view gradient turned the admin's
     blue buttons purple -- the opposite of converging on one colour. The ramp
     is now built around the theme's own primary.
     #4063F4 is 4.9:1 on white and #2B4CE0 is 6.5:1, so both clear AA for text. */
  --brand-50:#eef1fe; --brand-100:#dde3fd; --brand-300:#9fb1f9;
  --brand-500:#4063f4; --brand-600:#2b4ce0; --brand-700:#1f3ab8;

  /* Second stop of the brand gradient: a lighter tint of the same hue, so a
     gradient reads as one colour with depth rather than a slide between two. */
  --brand-accent:#6d8bff;
  --brand-gradient:linear-gradient(135deg,var(--brand-600) 0%,var(--brand-accent) 100%);

  /* Channel lists for the tints. Several views wrote #667eea20 (an 8-digit
     hex with alpha); CSS cannot apply alpha to a var() holding a hex, so the
     alpha forms go through rgba() instead. Keep in sync with the two above. */
  --brand-500-rgb:64,99,244;
  --brand-accent-rgb:109,139,255;

  /* Neutrals — deliberately NOT the theme's warm greys. Its muted tone is
     #a8aaae, which is 2.4:1 on white and fails the contrast floor this file
     exists to hold. The slate ramp below stays. */
  --n-0:#fff;      --n-50:#f8fafc;  --n-100:#f1f5f9; --n-200:#e2e8f0;
  --n-300:#cbd5e1; --n-400:#94a3b8; --n-500:#64748b; --n-600:#475569;
  --n-700:#334155; --n-800:#1e293b; --n-900:#0f172a;

  /* Semantic — these are the theme's own values, not a second opinion.
     The first cut of this file picked #10b981 / #ef4444 / #f59e0b / #0ea5e9,
     which appear exactly 0 times in the theme, while the theme paints its own
     across 346 rules (#28c76f x93, #ea5455 x103, #ff9f43 x76, #00cfe8 x74).
     That put two greens and two reds on the same screen. The minority gives
     way: adopting the theme's values converges everything with one edit,
     instead of repainting hundreds of theme rules.
     Tints are rgba of the same hue, so they always harmonise with it. */
  --success:#28c76f; --success-bg:rgba(40,199,111,.12);
  --warning:#ff9f43; --warning-bg:rgba(255,159,67,.12);
  --danger:#ea5455;  --danger-bg:rgba(234,84,85,.12);
  --info:#00cfe8;    --info-bg:rgba(0,207,232,.12);

  /* Surfaces and text */
  --surface:var(--n-0); --surface-2:var(--n-50);
  --border:var(--n-200); --text:var(--n-800);

  /* --text-muted is the lightest colour allowed on text: 4.76:1 on white.
     The views previously used #9ca3af / #a0aec0 / #94a3b8, all under 3:1. */
  --text-muted:var(--n-500);

  /* Radius */
  --r-sm:8px; --r-md:12px; --r-lg:16px; --r-xl:24px; --r-pill:999px;

  /* Elevation — only three, never invent a fourth */
  --e-1:0 1px 2px rgba(15,23,42,.06);
  --e-2:0 4px 12px rgba(15,23,42,.08);
  --e-3:0 12px 32px rgba(15,23,42,.12);

  /* Spacing — 4px base */
  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px;
  --s-5:24px; --s-6:32px; --s-8:48px; --s-10:64px;

  /* Type */
  --fs-xs:.75rem; --fs-sm:.8125rem; --fs-base:.9375rem;
  --fs-lg:1.0625rem; --fs-xl:1.25rem; --fs-2xl:1.5rem; --fs-3xl:2rem;
  --lh-tight:1.25; --lh-base:1.6;

  /* Motion */
  --t-fast:120ms; --t-base:200ms; --ease:cubic-bezier(.4,0,.2,1);
}

/* A visible focus target for keyboard users. Several views set outline:none on
   inputs; this restores a ring without touching those rules. */
a:focus-visible,
button:focus-visible,
.btn:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline:2px solid var(--brand-500);
  outline-offset:2px;
  box-shadow:0 0 0 3px var(--brand-100);
}

/* iOS zooms a form field on focus when its font-size is under 16px. The page
   level <style> blocks set 13-15px with class specificity, so this narrow
   override is the one sanctioned use of !important in the project. */
@media (max-width:575.98px) {
  input, select, textarea, .form-control, .form-select { font-size:16px !important; }
}

/* Two global rules were tried here and removed again, deliberately:
 *
 *   img, video, canvas { max-width:100%; height:auto }
 *     `height:auto` overrides an <img height="..."> attribute, so it silently
 *     resized logos and icons that had no CSS height of their own. The audit
 *     that motivated it found no element actually wider than 360px, so it was
 *     fixing a problem that did not exist.
 *
 *   @media (max-width:767.98px) { :not(.table-responsive) > table.table {
 *       display:block; overflow-x:auto } }
 *     Ten client views do render a <table> with no .table-responsive wrapper,
 *     but `display:block` on a table drops its column model and was never
 *     checked on a real phone. Fix those ten views with a proper wrapper
 *     instead of reshaping every table in the product from here.
 *
 * Global element selectors in this file reach three brands and thousands of
 * users. Nothing goes in without being seen rendering first. */

/* The theme's :root block sets --bs-link-color: #7367f0 and a hover shade, so
   every link in the product stays the theme's purple however the brand is set.
   tools/make-theme-brand.py cannot pick this up: the declarations sit in the
   same block as the whole Bootstrap variable set, which it skips rather than
   re-emit in full. Handled here instead. */
:root {
  --bs-link-color: var(--brand-600, #7367f0);
  --bs-link-hover-color: var(--brand-700, #685dd8);
}

/* Same reason: this one is a utility rather than a themed component, and pairs
   a primary fill with its own foreground. */
.text-bg-primary {
  background-color: var(--brand-600, #4063f4) !important;
  color: #fff !important;
}

/* DataTables' own processing box is absolutely positioned with a negative
   margin, so it drifts as the wrapper's height changes on redraw and a custom
   spinner lands off-centre. Pin it and take it out of the layout flow. */
.dataTables_wrapper { position:relative; }
.dataTables_wrapper .dataTables_processing {
  position:absolute;
  top:50%; left:50%;
  transform:translate(-50%,-50%);
  width:auto; height:auto;
  margin:0; padding:var(--s-3) var(--s-4);
  background:var(--surface, #fff);
  border:1px solid var(--border, #e2e8f0);
  border-radius:var(--r-md, 12px);
  box-shadow:var(--e-2, 0 4px 12px rgba(15,23,42,.08));
  z-index:10;
}

/* Two loaders show at once -- a spinning ring and a row of dots. That is
   DataTables doing it, not a clash between libraries. Its builder is:

     function _fnFeatureHtmlProcessing ( settings ) {
       return $('<div/>', { 'class': settings.oClasses.sProcessing })
         .html( settings.oLanguage.sProcessing )
         .append('<div><div></div><div></div><div></div><div></div></div>')
         .insertBefore( settings.nTable )[0];
     }

   `language.processing` is only meant to supply the *text*; the dot loader is
   appended unconditionally on top of it. The pages here pass a Bootstrap
   .spinner-border as that message, so the box ends up holding both.

   The appended loader is the last child and is the only div in the box with no
   class attribute, which identifies it exactly. It is hidden only where a
   custom spinner is present, so a table that leaves the default message alone
   keeps DataTables' own animation. */
.dataTables_wrapper .dataTables_processing:has(> .spinner-border) > div:last-child:not([class]) {
  display:none;
}

/* Keep the supplied spinner at its intended size regardless of what the
   DataTables stylesheet does to the children of this box. */
.dataTables_wrapper .dataTables_processing > .spinner-border {
  position:static;
  width:2rem; height:2rem;
  margin:0;
  border-radius:50%;
  vertical-align:middle;
}

@media (prefers-reduced-motion:reduce) {
  *, *::before, *::after {
    animation-duration:.01ms !important;
    transition-duration:.01ms !important;
  }
}

/* Bảng sản phẩm gộp ở /admin/devices/box-phone: hàng con phải đọc ra ngay là
   phụ thuộc hàng trên, nên nền chìm xuống một bậc thay vì thụt lề đơn thuần
   — thụt lề một mình biến mất khi bảng cuộn ngang. */
#boxProductTable > tbody > tr.js-pool-member > td {
    background-color: rgba(var(--brand-500-rgb, 64, 99, 244), .04);
    box-shadow: inset 3px 0 0 0 var(--brand-500, #4063f4);
}
#boxProductTable > tbody > tr.js-product-row + tr.js-pool-member > td { border-top: 0; }
