/*
 * Hand-written CSS for what this portal added after Tailwind was last compiled.
 *
 * assets/tailwind.css is built by a tool (see tailwind.config.js) that nobody
 * running this on cPanel has, so a brand-new utility class in a page would
 * simply do nothing. Anything needed that the compiled file does not already
 * carry goes here instead — plain CSS, loaded right after it, no build step.
 *
 * The component classes themselves (.card, .btn, .inp, .lbl, table.dt) are
 * defined in inc/header.php's own <style> block, which is why they work on
 * every page whether Tailwind was rebuilt or not.
 */

/* ══════════════════════════════════════════════════════════════════════════
   THE BRAND COLOUR
   ══════════════════════════════════════════════════════════════════════════

   assets/tailwind.css was compiled for the shop package this portal grew out
   of, and that build remaps the whole indigo and violet scales onto green (see
   assets/tailwind.config.js). So every `bg-indigo-600` in the markup currently
   paints the same green a POS does — which is why the two looked like the same
   application wearing the same clothes.

   Rather than rewrite `indigo` out of forty files, the scale is redefined here,
   once. isp.css loads after tailwind.css, the selectors are identical, so these
   win. Change the seven hex values below and the whole portal changes colour.

   ── Why blue ───────────────────────────────────────────────────────────────
   It is what the product is about: a line, a signal, a connection. And it frees
   green to mean one thing only — money that came in. The system now reads:

       blue      the brand, and anything you can press
       emerald   money received, a line that is up, a healthy figure
       rose      money owed, a line that is down, a destructive button
       amber     something that needs attention but is not yet wrong
       slate     structure and everything switched off

   Emerald is deliberately NOT redefined: it keeps its own meaning, and a
   collection figure sitting in green beside a blue button is the whole point.
   ────────────────────────────────────────────────────────────────────────── */

.bg-indigo-50      { background-color: #eff6ff }
.bg-indigo-50\/40  { background-color: rgba(239, 246, 255, .4) }
.bg-indigo-100     { background-color: #dbeafe }
.bg-indigo-500     { --tw-bg-opacity: 1; background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1)) }
.bg-indigo-600     { --tw-bg-opacity: 1; background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1)) }
.bg-indigo-700     { --tw-bg-opacity: 1; background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1)) }
.hover\:bg-indigo-700:hover { --tw-bg-opacity: 1; background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1)) }

.text-indigo-300   { --tw-text-opacity: 1; color: rgb(147 197 253 / var(--tw-text-opacity, 1)) }
.text-indigo-500   { --tw-text-opacity: 1; color: rgb(59 130 246 / var(--tw-text-opacity, 1)) }
.text-indigo-600   { --tw-text-opacity: 1; color: rgb(37 99 235 / var(--tw-text-opacity, 1)) }
.text-indigo-700   { --tw-text-opacity: 1; color: rgb(29 78 216 / var(--tw-text-opacity, 1)) }
.hover\:text-indigo-600:hover { --tw-text-opacity: 1; color: rgb(37 99 235 / var(--tw-text-opacity, 1)) }

.border-indigo-100 { --tw-border-opacity: 1; border-color: rgb(219 234 254 / var(--tw-border-opacity, 1)) }
.border-indigo-200 { --tw-border-opacity: 1; border-color: rgb(191 219 254 / var(--tw-border-opacity, 1)) }
.hover\:border-indigo-300:hover { --tw-border-opacity: 1; border-color: rgb(147 197 253 / var(--tw-border-opacity, 1)) }
.focus\:border-indigo-400:focus { --tw-border-opacity: 1; border-color: rgb(96 165 250 / var(--tw-border-opacity, 1)) }
.focus\:ring-indigo-100:focus   { --tw-ring-opacity: 1; --tw-ring-color: rgb(219 234 254 / var(--tw-ring-opacity, 1)) }

/* Gradients carry their stops in custom properties, so the whole triple has to
   be restated — setting only --tw-gradient-from leaves the old green "to". */
.from-indigo-50 {
  --tw-gradient-from: #eff6ff var(--tw-gradient-from-position);
  --tw-gradient-to: rgba(239, 246, 255, 0) var(--tw-gradient-to-position);
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.from-indigo-900 {
  --tw-gradient-from: #172554 var(--tw-gradient-from-position);
  --tw-gradient-to: rgba(23, 37, 84, 0) var(--tw-gradient-to-position);
  --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}

/* One stray violet, on the dashboard's bank card. */
.text-violet-600 { --tw-text-opacity: 1; color: rgb(79 70 229 / var(--tw-text-opacity, 1)) }

/* ── The sidebar ──────────────────────────────────────────────────────────
   A navy with a blue cast rather than Tailwind's neutral slate-900, so the
   column reads as part of the brand instead of as a grey gutter. The classes
   keep their names; only what they paint changes. */
aside#sidebar             { background-color: #0d1526 }
aside#sidebar .border-slate-800 { border-color: #1b2740 }
aside#sidebar a:hover,
aside#sidebar button:hover { background-color: #17223a }

/* The active item gets a bar down its left edge as well as a fill. On a long
   menu the fill alone is easy to lose; the bar is visible from the corner of
   the eye, which is how a sidebar is actually read.

   It used to carry a blue glow underneath as well. On the dark column that
   read as a smudge around the item rather than as depth — and against a navy
   this dark, a coloured shadow has nothing to fall on. The fill and the bar
   say "here" on their own. */
aside#sidebar a.bg-indigo-600 {
  position: relative;
  box-shadow: none;
}
aside#sidebar a.bg-indigo-600::before {
  content: "";
  position: absolute;
  left: -12px; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 22px;
  border-radius: 0 3px 3px 0;
  background: #93c5fd;
}

/* ── Numbers line up in a column ──────────────────────────────────────────
   Money and dates in a table read as a column only when every digit is the
   same width. Applied to the whole table rather than to each cell, because a
   right-aligned amount is the commonest thing in this application. */
table.dt td,
table.dt th {
  font-variant-numeric: tabular-nums;
}

/* ── The print-only header on a report ────────────────────────────────────
   Tailwind's print: variants are not in the compiled file, so the report
   pages' "hidden print:block" title block needs this by hand. */
.print\:block { display: none; }

@media print {
  .no-print { display: none !important; }
  .print\:block { display: block !important; }

  /* A report is read across, so let it use the paper. The panel's own padding
     and the sidebar are gone by then anyway. */
  body { background: #fff; }
  .card {
    border: 1px solid #e2e8f0 !important;
    box-shadow: none !important;
    break-inside: avoid;
  }
  /* A table split across a page break loses its heading; repeat it. */
  table.dt thead { display: table-header-group; }
  table.dt tr    { break-inside: avoid; }

  @page { margin: 12mm; }
}

/* ── A long list that scrolls inside its own card ─────────────────────────
   bill_generate.php's preview and the router log cap their height so the
   buttons underneath stay reachable. max-h-[26rem] is an arbitrary value
   Tailwind would have had to be rebuilt to know about. */
.table-wrap.max-h-\[26rem\] { max-height: 26rem; overflow-y: auto; }

/* ── Bulk-action rows ─────────────────────────────────────────────────────
   The overdue list tints a row that is past its grace period. bg-rose-50/40 is
   an opacity variant the compiled file may not carry. */
.bg-rose-50\/40 { background-color: rgba(255, 241, 242, .4); }

/* ── The subscriber id, PPPoE name and IP ─────────────────────────────────
   A monospace face, so a technician reading a column of PPPoE users down the
   page can see at a glance which two differ by one character. */
.font-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

/* ── Colours built at runtime ─────────────────────────────────────────────
   badge(), status_badge() and the dashboard's stat_card() compose their class
   names from a colour argument ("bg-" . $color . "-100"), so Tailwind's scanner
   never saw them and the compiled file may carry no rule. Every palette those
   three can ask for is written out here, once.

   If you add a colour to one of those functions, add its three lines here. */
.bg-slate-100   { background-color: #f1f5f9 }  .text-slate-700   { color: #334155 }
.bg-emerald-100 { background-color: #d1fae5 }  .text-emerald-700 { color: #047857 }
.bg-amber-100   { background-color: #fef3c7 }  .text-amber-700   { color: #b45309 }
.bg-rose-100    { background-color: #ffe4e6 }  .text-rose-700    { color: #be123c }
.bg-indigo-100  { background-color: #e0e7ff }  .text-indigo-700  { color: #4338ca }
.bg-orange-100  { background-color: #ffedd5 }  .text-orange-700  { color: #c2410c }
.bg-sky-100     { background-color: #e0f2fe }  .text-sky-700     { color: #0369a1 }
.bg-teal-100    { background-color: #ccfbf1 }  .text-teal-700    { color: #0f766e }
.bg-violet-100  { background-color: #ede9fe }  .text-violet-700  { color: #6d28d9 }

.text-slate-500   { color: #64748b }
.text-emerald-500 { color: #10b981 }
.text-amber-500   { color: #f59e0b }
.text-rose-500    { color: #f43f5e }
.text-indigo-500  { color: #6366f1 }
.text-orange-500  { color: #f97316 }
.text-sky-500     { color: #0ea5e9 }
.text-teal-500    { color: #14b8a6 }
.text-violet-500  { color: #8b5cf6 }

.hover\:border-slate-300:hover   { border-color: #cbd5e1 }
.hover\:border-emerald-300:hover { border-color: #6ee7b7 }
.hover\:border-amber-300:hover   { border-color: #fcd34d }
.hover\:border-rose-300:hover    { border-color: #fda4af }
.hover\:border-indigo-300:hover  { border-color: #a5b4fc }
.hover\:border-orange-300:hover  { border-color: #fdba74 }
.hover\:border-sky-300:hover     { border-color: #7dd3fc }
.hover\:border-teal-300:hover    { border-color: #5eead4 }
.hover\:border-violet-300:hover  { border-color: #c4b5fd }

/* The flash bar composes border/background/text from one colour too. */
.border-emerald-200 { border-color: #a7f3d0 }  .bg-emerald-50 { background-color: #ecfdf5 }  .text-emerald-800 { color: #065f46 }
.border-rose-200    { border-color: #fecdd3 }  .bg-rose-50    { background-color: #fff1f2 }  .text-rose-800    { color: #9f1239 }
.border-amber-200   { border-color: #fde68a }  .bg-amber-50   { background-color: #fffbeb }  .text-amber-800   { color: #92400e }
.border-sky-200     { border-color: #bae6fd }  .bg-sky-50     { background-color: #f0f9ff }  .text-sky-800     { color: #075985 }
.border-slate-200   { border-color: #e2e8f0 }  .bg-slate-50   { background-color: #f8fafc }  .text-slate-800   { color: #1e293b }

/* ── A delete button that is there but cannot be pressed ──────────────────
   del_btn() draws one dimmed, with the reason on hover, for a row that may not
   be deleted — an account with transactions behind it, say. opacity-30 is the
   one utility that markup needs which the compiled file does not carry. */
.opacity-30 { opacity: .3 }

/* ── Screen-reader-only, for the settings sliders ──────────────────────────
   The checkbox behind each slider is hidden with .sr-only and still has to be
   focusable, so it is positioned away rather than display:none. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* ── A dialog ─────────────────────────────────────────────────────────────
   Plain CSS with names of its own rather than a stack of utility classes:
   tailwind.css here is a fixed build with no toolchain behind it, so a class
   that was never compiled does nothing at all — and a dialog whose backdrop
   quietly fails to appear is worse than no dialog.

   Scrolling lives on the overlay, not the box: a long form inside a box with
   its own scrollbar traps the wheel and hides the buttons at the bottom. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  align-items: flex-start;
  justify-content: center;
  padding: 24px 16px;
  overflow-y: auto;
  background: rgba(9, 15, 28, .55);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.modal.open { display: flex }
body.modal-open { overflow: hidden }

.modal-box {
  width: 100%;
  max-width: 1000px;
  margin: auto;
  background: #fff;
  border-radius: 18px;
  box-shadow: 0 40px 80px -30px rgba(13, 21, 38, .6);
  animation: modal-in .18s ease-out;
}
@keyframes modal-in {
  from { opacity: 0; transform: translateY(-10px) }
  to   { opacity: 1; transform: none }
}
@media (prefers-reduced-motion: reduce) { .modal-box { animation: none } }

/* The header stays put while a long form scrolls past it, so the way out is
   never somewhere above the top of the screen. */
.modal-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 15px 20px;
  border-bottom: 1px solid #e6eaf1;
  background: #fff;
  border-radius: 18px 18px 0 0;
}
.modal-head h3 { margin: 0; font-size: 15px; font-weight: 600; color: #1e293b }
.modal-head .sub { font-size: 12px; color: #94a3b8 }

.modal-x {
  margin-left: auto;
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 10px;
  background: transparent; color: #64748b;
  cursor: pointer;
}
.modal-x:hover { background: #f1f5f9; color: #0f172a }

.modal-body { padding: 20px }
.modal-body > * + * { margin-top: 20px }

.modal-foot {
  position: sticky;
  bottom: 0;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 14px 20px;
  border-top: 1px solid #e6eaf1;
  background: #f8fafc;
  border-radius: 0 0 18px 18px;
}

/* The pencil in the corner of a card. Faint until the card is under the
   pointer, so it does not compete with the information it sits beside. */
.card-edit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  flex: none;
  border: 1px solid #e6eaf1;
  border-radius: 9px;
  background: #fff;
  color: #94a3b8;
  cursor: pointer;
  transition: .15s;
}
.card-edit:hover { color: #2563eb; border-color: #bfd7fd; background: #f5f9ff }

/* The invoice inside a dialog. A fixed share of the window rather than a
   height measured from the frame's own content: reading that across a frame
   needs script, and a dialog whose height depends on script is a dialog that
   collapses to nothing when the load is slow. */
.bill-frame {
  display: block;
  width: 100%;
  height: 72vh;
  border: 0;
  background: #fff;
}
@media (max-width: 640px) { .bill-frame { height: 64vh } }

/* The subscriber form, in a dialog. Taller than the invoice: it is a form
   somebody is filling in, and a short frame means scrolling inside a box that
   is itself inside a page. */
.form-frame {
  display: block;
  width: 100%;
  height: 78vh;
  border: 0;
  background: #fff;
  border-radius: 0 0 18px 18px;
}
@media (max-width: 640px) { .form-frame { height: 82vh } }

@media print { .modal { display: none !important } }

/* ── A chip that reports, beside buttons that act ─────────────────────────
   Used in list rows for the line and the account state. Deliberately softer
   than .btn: a paler border, a tinted ground, no hover and no pointer. On a
   row that also carries an Edit button the two must not look alike, or the
   office learns to click things that do nothing — and then hesitates over the
   one that does. */
.chip {
  display: inline-flex;
  align-items: center;
  padding: 5px 13px;
  border: 1px solid #e9edf4;
  /* A pill, not a rounded box. The buttons beside it are 9px-cornered
     rectangles; the different shape is what stops the eye from reading this
     as another thing to click. */
  border-radius: 999px;
  background: #fbfcfe;
  color: #64748b;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  cursor: default;
}
/* The dot a chip can carry. On a live line it breathes — a halo that grows and
   fades once every couple of seconds. Slow on purpose: this sits in a list
   where forty of them may be on screen, and anything quicker turns the page
   into a switchboard. It is the ring that animates, not the dot, so nothing
   reflows and the text beside it never shifts. */
.chip-dot {
  width: 7px; height: 7px;
  margin-right: 7px;
  border-radius: 999px;
  background: currentColor;
  flex: none;
}
.chip-dot-live { animation: chip-pulse 2.4s ease-in-out infinite }
@keyframes chip-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(4, 120, 87, .45) }
  50%      { box-shadow: 0 0 0 5px rgba(4, 120, 87, 0) }
}
@media (prefers-reduced-motion: reduce) { .chip-dot-live { animation: none } }

.chip-on   { border-color: #c7f0dd; background: #f3fdf8; color: #047857 }
.chip-off  { border-color: #fbd5da; background: #fff5f6; color: #be123c }
.chip-warn { border-color: #f7e2b8; background: #fffaf1; color: #b45309 }

/* ── A switch that is really a submit button ───────────────────────────────
   Not a checkbox driven by script: this one cuts somebody's internet off, and
   a control that needs JavaScript to have any effect is a control that lies
   about its state the moment the script fails to load. A <button> inside a
   form posts, the page comes back, and what is drawn is what the database
   actually says. */
.switch {
  position: relative;
  /* Spelled out because this is worn by a <button> on the subscriber's page
     and by an <a> in the list, and an anchor is inline by default — width and
     height would simply be ignored on it. */
  display: inline-block;
  vertical-align: middle;
  width: 44px; height: 25px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 99px;
  background: #cbd5e1;
  cursor: pointer;
  transition: background .18s;
}
.switch .knob {
  position: absolute;
  top: 3px; left: 3px;
  width: 19px; height: 19px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(13, 21, 38, .35);
  transition: transform .18s;
}
.switch.is-on            { background: #059669 }

/* The portal sign-in switch. Blue rather than green, because the page carries
   two switches and the green one cuts somebody's internet off — they must not
   be told apart by position alone. */
.switch-blue.is-on       { background: #2563eb }
.switch.is-on .knob      { transform: translateX(19px) }
.switch:hover            { filter: brightness(.96) }
.switch:focus-visible    { outline: 2px solid #2563eb; outline-offset: 2px }
.switch[disabled]        { opacity: .5; cursor: not-allowed; filter: none }

@media (prefers-reduced-motion: reduce) {
  .switch, .switch .knob { transition: none }
}
