/* =============================================================================
   Smart Home design tokens and shell styling (2026-09-17)
   -----------------------------------------------------------------------------
   Loaded after appStyle.css. It does three jobs:

   1. Defines a real token set. The app had five custom properties and roughly a
      thousand hardcoded colour literals spread over twelve stylesheets and
      fourteen inline <style> blocks, so nothing could be restyled in one place.
   2. Re-points the five legacy variables at the new tokens, so the 1,950 lines
      of appStyle.css follow the new palette without being rewritten.
   3. Modernises the shared shell (header, bottom nav, cards, buttons, forms,
      modals, toasts) and fixes the mobile defects: the bottom bar sat under the
      Android gesture bar, there was no focus ring, no tap-highlight control,
      and the Bengali font the stylesheets asked for was never actually loaded.

   Dark mode lives in theme-dark.css, which is generated from the existing
   stylesheets. Keep that file next to this one.
   ============================================================================= */

/* Bengali and Latin faces. Every stylesheet already asked for Noto Sans Bengali
   but nothing ever loaded it, so Bengali fell back to whatever the device had
   and desktop browsers rendered it badly. */
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Bengali:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap');

:root {
  color-scheme: light;

  /* ---- brand ---- */
  --sh-accent:          #2b8fd6;
  --sh-accent-weak:     #eaf4fd;
  --sh-warm:            #f5a524;
  --sh-warm-soft:       #fef4e2;
  --sh-accent-strong:   #1f6fa8;
  --sh-accent-soft:     #e8f3fd;
  --sh-accent-border:   #bcdcf5;
  /* Translucent accent, for glows and coloured shadows. */
  --sh-accent-glow:     rgba(43, 143, 214, .30);
  --sh-accent-glow-soft: rgba(43, 143, 214, .14);

  /* ---- surfaces ---- */
  --sh-bg:              #f5f8fc;
  --sh-surface:         #ffffff;
  --sh-surface-2:       #f8fafd;
  --sh-surface-sunken:  #edf2f8;
  --sh-tint-from:       #e8f3fd;
  --sh-tint-to:         #d3e9fb;
  /* Page mesh: two soft radial glows over --sh-bg (see `body` below). */
  --sh-bg-glow-1:       rgba(43, 143, 214, .16);
  --sh-bg-glow-2:       rgba(245, 165, 36, .10);
  /* Translucent surfaces for the floating header / nav / toasts. */
  --sh-glass:           rgba(255, 255, 255, .78);
  --sh-glass-strong:    rgba(255, 255, 255, .92);
  /* Full-screen scrim behind a busy overlay. */
  --sh-scrim:           rgba(15, 27, 40, .55);
  /* Shimmer for skeleton placeholders. */
  --sh-skeleton:        #e6edf5;
  --sh-skeleton-shine:  rgba(255, 255, 255, .75);
  /* Home hero panel. */
  --sh-hero-from:       #eaf4fd;
  --sh-hero-to:         #ffffff;

  /* ---- text ---- */
  --sh-text:            #0f1b28;
  --sh-text-muted:      #5a6b7d;
  --sh-text-faint:      #6f8295;
  --sh-text-on-accent:  #ffffff;
  --sh-heading:         #123c58;

  /* ---- lines ---- */
  --sh-border:          #e4eaf2;
  --sh-border-strong:   #ccd8e6;

  /* ---- status ---- */
  --sh-success:         #1d8a5e;
  --sh-success-soft:    #e6f6ee;
  --sh-danger:          #c0392b;
  --sh-danger-soft:     #fdecea;
  --sh-warning:         #96620f;
  --sh-warning-soft:    #fdf3e2;
  --sh-info:            var(--sh-accent);
  --sh-info-soft:       var(--sh-accent-soft);

  /* ---- shape ---- */
  --sh-r-sm:   8px;
  --sh-r-md:   12px;
  --sh-r-lg:   18px;
  --sh-r-xl:   24px;
  --sh-r-pill: 999px;

  /* ---- elevation ---- */
  --sh-shadow-1: 0 1px 2px rgba(15, 27, 40, .05), 0 1px 3px rgba(15, 27, 40, .04);
  --sh-shadow-2: 0 1px 2px rgba(15, 27, 40, .04), 0 4px 10px rgba(15, 27, 40, .06), 0 12px 24px rgba(15, 27, 40, .05);
  --sh-shadow-3: 0 2px 4px rgba(15, 27, 40, .05), 0 12px 28px rgba(15, 27, 40, .10), 0 24px 48px rgba(15, 27, 40, .08);
  --sh-ring:     0 0 0 3px rgba(43, 143, 214, .35);

  /* ---- rhythm ---- */
  --sh-1: 4px;  --sh-2: 8px;  --sh-3: 12px; --sh-4: 16px;
  --sh-5: 24px; --sh-6: 32px; --sh-7: 48px;

  /* ---- type ---- */
  --sh-font: 'Noto Sans Bengali', 'Inter', 'Segoe UI', Roboto, system-ui, -apple-system, sans-serif;
  --sh-fs-xs: .75rem; --sh-fs-sm: .875rem; --sh-fs-md: 1rem;
  --sh-fs-lg: 1.125rem; --sh-fs-xl: 1.375rem; --sh-fs-2xl: 1.75rem;

  --sh-ease: cubic-bezier(.22, .61, .36, 1);
  --sh-fast: 140ms;
  --sh-slow: 280ms;

  /* Shell geometry. The bottom bar is a floating pill now: --sh-nav-gap is the
     gap between the pill and the screen edge, and --sh-nav-offset is the total
     height a page has to clear (pill + gap + the Android gesture area). */
  --sh-header-h: 64px;
  --sh-navbar-h: 64px;
  --sh-nav-h: var(--sh-navbar-h);
  --sh-nav-gap: 12px;
  --sh-safe-top: env(safe-area-inset-top, 0px);
  --sh-safe-bottom: env(safe-area-inset-bottom, 0px);
  --sh-nav-offset: calc(var(--sh-navbar-h) + var(--sh-nav-gap) + var(--sh-safe-bottom));

  /* ---- legacy names, kept so appStyle.css inherits the new palette ---- */
  --primary-bg-color:   var(--sh-tint-from);
  --secondary-bg-color: var(--sh-accent-border);
  --text-color:         var(--sh-text);
  --icon-color:         var(--sh-accent);
  --txt-title-color:    var(--sh-heading);
}

/* Dark values for the tokens introduced above. theme-dark.css is generated
   and only knows the original token set, so these live here; the generated
   file's own :root[data-theme="dark"] block does not touch them. */
:root[data-theme="dark"] {
  --sh-accent-glow:      rgba(74, 168, 232, .32);
  --sh-accent-glow-soft: rgba(74, 168, 232, .14);
  --sh-bg-glow-1:        rgba(74, 168, 232, .14);
  --sh-bg-glow-2:        rgba(245, 181, 68, .07);
  --sh-glass:            rgba(19, 28, 39, .78);
  --sh-glass-strong:     rgba(19, 28, 39, .92);
  --sh-scrim:            rgba(0, 0, 0, .65);
  --sh-skeleton:         #1c2837;
  --sh-skeleton-shine:   rgba(255, 255, 255, .06);
  --sh-hero-from:        #16263a;
  --sh-hero-to:          #131c27;
}

/* ---------------------------------------------------------------- base ---- */

/* On phones the app should feel like an app: no text selection handles, no
   copy bubble, no long-press callout on links and images. Form fields stay
   selectable, and anything marked .sh-selectable (a code the user is meant to
   copy) does too. Desktop keeps normal selection. */
@media (hover: none) and (pointer: coarse) {
  body {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }
  input, textarea, select, [contenteditable="true"], .sh-selectable,
  .sh-selectable * {
    -webkit-user-select: text;
    user-select: text;
  }
  img, a { -webkit-touch-callout: none; -webkit-user-drag: none; }
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  /* A quiet mesh rather than a flat grey: two soft glows pinned to the top
     corners, over the base colour. Both stops are tokens, so the dark theme
     gets deep-navy glows without a generated rule. */
  background-color: var(--sh-bg);
  background-image:
    radial-gradient(60% 40% at 0% 0%,   var(--sh-bg-glow-1) 0%, transparent 70%),
    radial-gradient(50% 35% at 100% 8%, var(--sh-bg-glow-2) 0%, transparent 70%);
  /* Not `fixed`: mobile Chrome ignores it and repaints the mesh in
     strips while scrolling. */
  background-attachment: scroll;
  background-repeat: no-repeat;
  color: var(--sh-text);
  font-family: var(--sh-font);
  font-size: var(--sh-fs-md);
  line-height: 1.6;
  /* The grey flash on every tap is the single most "not an app" tell. */
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: contain;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--sh-font);
  color: var(--sh-heading);
  font-weight: 600;
  line-height: 1.3;
}

a { color: var(--sh-accent-strong); }
a:hover { color: var(--sh-accent); }

hr { border-color: var(--sh-border); opacity: 1; }

/* One visible focus ring everywhere, for keyboard users only. */
:focus-visible {
  outline: none;
  box-shadow: var(--sh-ring);
  border-radius: var(--sh-r-sm);
}

::selection {
  background: var(--sh-accent-soft);
  color: var(--sh-accent-strong);
}

/* Scrollbars that match the surface instead of the OS default. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--sh-border-strong) transparent;
}
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb {
  background: var(--sh-border-strong);
  border-radius: var(--sh-r-pill);
}
*::-webkit-scrollbar-track { background: transparent; }

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

/* -------------------------------------------------------------- header ---- */

/* `body > header` on purpose. Pages render their own <header class="sh-page-head">
   and <header class="dc-hero"> inside <main>; a bare `header` selector painted
   those as a second sticky gradient band and the grid scrolled up under it. */
body > header {
  background: var(--sh-glass);
  border-bottom: 1px solid var(--sh-border);
  box-shadow: var(--sh-shadow-1);
  padding: 0 var(--sh-4);
  padding-top: var(--sh-safe-top);
  backdrop-filter: saturate(160%) blur(14px);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
}

body .wifi-status-btn {
  border-radius: var(--sh-r-pill);
  transition: transform var(--sh-fast) var(--sh-ease),
              background-color var(--sh-fast) var(--sh-ease);
}
body .wifi-status-btn:active { transform: scale(.94); }

body .wifi-status-btn.connected   { color: var(--sh-success); }
body .wifi-status-btn.disconnected { color: var(--sh-danger); }
body .wifi-status-btn.checking     { color: var(--sh-text-faint); }

/* ---------------------------------------------------------- bottom nav ---- */

/* The bar used to sit flush at bottom:0, so on a gesture-navigation Android
   phone the labels were behind the system pill. Pages compensated with magic
   margins of 70/90/110px; the padding below replaces all of that. */
body .bottom-nav {
  background: var(--sh-glass-strong);
  backdrop-filter: saturate(160%) blur(16px);
  -webkit-backdrop-filter: saturate(160%) blur(16px);
  border: 1px solid var(--sh-border);
  box-shadow: var(--sh-shadow-3);
  padding: 0;
}

body .nav-item {
  color: var(--sh-text-faint);
  border-radius: var(--sh-r-pill);
  transition: color var(--sh-fast) var(--sh-ease),
              background-color var(--sh-fast) var(--sh-ease),
              transform var(--sh-fast) var(--sh-ease);
}
body .nav-item:active { transform: scale(.93); }
body .nav-item.active,
body .nav-item:hover { color: var(--sh-accent-strong); }
body .nav-item.active { background: var(--sh-accent-soft); }

body .nav-item.nav-center .nav-icon-center {
  background: linear-gradient(135deg, var(--sh-accent) 0%, var(--sh-accent-strong) 100%);
  color: var(--sh-text-on-accent);
  box-shadow: 0 8px 22px var(--sh-accent-glow);
}

/* Give every page room for the floating bar instead of per-page magic numbers. */
body main,
body #main-content {
  padding-bottom: calc(var(--sh-nav-offset) + var(--sh-6));
}

/* --------------------------------------------------------------- cards ---- */

body .device-card,
body .summary-card,
body .stat-card,
body .profile-card,
body .chat-card {
  background: var(--sh-surface);
  border: 1px solid var(--sh-border);
  border-radius: var(--sh-r-lg);
  box-shadow: var(--sh-shadow-1);
  transition: transform var(--sh-slow) var(--sh-ease),
              box-shadow var(--sh-slow) var(--sh-ease),
              border-color var(--sh-slow) var(--sh-ease);
}

@media (hover: hover) {
  body .device-card:hover {
    transform: translateY(-3px);
    box-shadow: var(--sh-shadow-2);
    border-color: var(--sh-accent-border);
  }
}
body .device-card:active { transform: translateY(0) scale(.985); }

body .device-control-card {
  background: var(--sh-surface);
  border: 1px solid var(--sh-border);
  border-radius: var(--sh-r-lg);
  box-shadow: var(--sh-shadow-1);
}

body .dv-heading {
  border-bottom: 1px solid var(--sh-border);
  padding-bottom: var(--sh-2);
}
body .dv-heading h2 {
  color: var(--sh-heading);
  font-size: var(--sh-fs-xl);
  letter-spacing: -.01em;
}

/* ------------------------------------------------------------- buttons ---- */

/* btn-on / btn-off name the action, not the state: btn-on is shown while the
   port is OFF. The colours follow the action so the label and the tint agree. */
body .btn-on,
body .btn-off {
  border: 1px solid transparent;
  border-radius: var(--sh-r-pill);
  padding: 10px 22px;
  font-size: var(--sh-fs-md);
  font-weight: 600;
  min-height: 44px;                 /* comfortable touch target */
  box-shadow: var(--sh-shadow-1);
  transition: transform var(--sh-fast) var(--sh-ease),
              box-shadow var(--sh-fast) var(--sh-ease),
              filter var(--sh-fast) var(--sh-ease);
}
body .btn-on {
  background: var(--sh-success-soft);
  color: var(--sh-success);
  border-color: rgba(29, 138, 94, .22);
}
body .btn-off {
  background: var(--sh-danger-soft);
  color: var(--sh-danger);
  border-color: rgba(192, 57, 43, .22);
}
body .btn-on:hover,
body .btn-off:hover { filter: brightness(.97); box-shadow: var(--sh-shadow-2); }
body .btn-on:active,
body .btn-off:active { transform: scale(.96); }
body .btn-on[disabled],
body .btn-off[disabled] { opacity: .55; pointer-events: none; }

body .btn-primary {
  background: linear-gradient(135deg, var(--sh-accent) 0%, var(--sh-accent-strong) 100%);
  border: none;
  border-radius: var(--sh-r-pill);
  min-height: 44px;
  font-weight: 600;
  box-shadow: 0 4px 14px var(--sh-accent-glow);
}
body .btn-primary:hover { filter: brightness(1.05); }
body .btn-primary:active { transform: scale(.98); }

body .btn-outline-secondary {
  border-radius: var(--sh-r-pill);
  border-color: var(--sh-border-strong);
  color: var(--sh-text-muted);
  min-width: 40px;
  min-height: 40px;
}
body .btn-outline-secondary:hover {
  background: var(--sh-accent-soft);
  border-color: var(--sh-accent);
  color: var(--sh-accent-strong);
}

/* --------------------------------------------------------------- forms ---- */

body .form-control,
body .form-select,
body input[type="text"],
body input[type="number"],
body input[type="password"],
body input[type="tel"],
body input[type="time"],
body textarea {
  background: var(--sh-surface);
  border: 1px solid var(--sh-border-strong);
  border-radius: var(--sh-r-md);
  color: var(--sh-text);
  /* 16px keeps iOS/WebView from zooming the page on focus. */
  font-size: 16px;
  min-height: 46px;
  padding: 10px 14px;
  transition: border-color var(--sh-fast) var(--sh-ease),
              box-shadow var(--sh-fast) var(--sh-ease);
}
body .form-control::placeholder,
body textarea::placeholder { color: var(--sh-text-faint); }

body .form-control:focus,
body .form-select:focus,
body textarea:focus {
  border-color: var(--sh-accent);
  box-shadow: var(--sh-ring);
  outline: none;
}

body .form-label { color: var(--sh-text-muted); font-weight: 500; font-size: var(--sh-fs-sm); }

body .form-range { accent-color: var(--sh-accent); height: 32px; }
body .form-range::-webkit-slider-thumb {
  background: var(--sh-accent);
  box-shadow: 0 2px 6px var(--sh-accent-glow);
}
body .form-check-input:checked {
  background-color: var(--sh-accent);
  border-color: var(--sh-accent);
}

/* -------------------------------------------------- modals, toasts, etc ---- */

body .modal-content {
  background: var(--sh-surface);
  border: 1px solid var(--sh-border);
  border-radius: var(--sh-r-xl);
  box-shadow: var(--sh-shadow-3);
}
body .modal-header { border-bottom: 1px solid var(--sh-border); }
body .modal-footer { border-top: 1px solid var(--sh-border); }
body .modal-title { color: var(--sh-heading); }

/* Bottom sheets read as native on a phone. */
@media (max-width: 575.98px) {
  body .modal-dialog:not(.modal-dialog-centered) {
    margin: 0;
    position: fixed;
    left: 0; right: 0; bottom: 0;
  }
  body .modal-dialog:not(.modal-dialog-centered) .modal-content {
    border-radius: var(--sh-r-xl) var(--sh-r-xl) 0 0;
    padding-bottom: var(--sh-safe-bottom);
  }
}

body .alert { border-radius: var(--sh-r-md); border: 1px solid transparent; }
body .alert-success { background: var(--sh-success-soft); color: var(--sh-success); border-color: rgba(29, 138, 94, .2); }
body .alert-danger  { background: var(--sh-danger-soft);  color: var(--sh-danger);  border-color: rgba(192, 57, 43, .2); }
body .alert-warning { background: var(--sh-warning-soft); color: var(--sh-warning); border-color: rgba(183, 121, 31, .2); }

body .badge { border-radius: var(--sh-r-pill); font-weight: 600; }

body .table { --bs-table-bg: transparent; color: var(--sh-text); }
body .table > :not(caption) > * > * { border-bottom-color: var(--sh-border); }
body .table thead th {
  color: var(--sh-text-muted);
  font-weight: 600;
  font-size: var(--sh-fs-sm);
  text-transform: none;
}

body .list-group-item {
  background: var(--sh-surface);
  color: var(--sh-text);
  border-color: var(--sh-border);
}

/* ------------------------------------------------------ realtime status ---- */

/* ws-onmessage-event.js toggles these on <body> and on [data-device-presence]. */
[data-device-presence] {
  display: inline-flex;
  align-items: center;
  gap: var(--sh-2);
  font-size: var(--sh-fs-sm);
  font-weight: 500;
  padding: 4px 12px;
  border-radius: var(--sh-r-pill);
}
[data-device-presence]::before {
  content: '';
  width: 8px; height: 8px;
  border-radius: var(--sh-r-pill);
  background: currentColor;
}
[data-device-presence].is-online {
  color: var(--sh-success);
  background: var(--sh-success-soft);
}
[data-device-presence].is-offline {
  color: var(--sh-text-faint);
  background: var(--sh-surface-sunken);
}
[data-device-presence].is-online::before {
  animation: sh-pulse 2s var(--sh-ease) infinite;
}

@keyframes sh-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .45; transform: scale(.8); }
}

/* While the socket is down, controls stay visible but clearly inert. */
body.ws-disconnected .device-toggle-btn,
body.ws-disconnected .device-slider {
  opacity: .6;
}

/* --------------------------------------------------------------- misc ---- */

body #loaderOverlay { background: var(--sh-bg); }

img { max-width: 100%; height: auto; }

/* Wide content scrolls inside its own box rather than the page. */
body .table-responsive { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* Bigger hit areas for the small icon buttons on the control screen. */
body .btn-timer-icon,
body .btn-schedule-icon,
body .speed-decrease,
body .speed-increase {
  min-width: 40px;
  min-height: 40px;
  border-radius: var(--sh-r-md);
}

/* Device artwork is SVG now, drawn to read on both themes, and
   light-fan-controller.js owns those src values. Nothing to swap here. */

/* The language switch moved from the header into Settings (a segmented
   control in the Account card, see .sh-segmented.is-compact in appStyle.css),
   and the header avatar is gone with it. */

/* Bootstrap's .btn-close is a black SVG baked into a background-image; on a
   dark modal it vanished (reported from the schedule and timer sheets). Invert
   it in dark mode, everywhere. */
:root[data-theme="dark"] .btn-close {
  filter: invert(1) grayscale(100%) brightness(180%);
  opacity: .75;
}
:root[data-theme="dark"] .btn-close:hover,
:root[data-theme="dark"] .btn-close:focus { opacity: 1; }

/* ------------------------------------------ Bootstrap follows the tokens ----
   Bootstrap paints .modal-content, .text-muted, .form-text and friends from its
   own --bs-* colours, which knew nothing about the dark theme: a plain <div> or
   a .text-muted line inside a modal stayed #212529 on a navy surface and could
   not be read. Pointing those variables at the tokens fixes every such place at
   once, in both themes, because the tokens are what change. */
:root {
  --bs-body-color: var(--sh-text);
  --bs-secondary-color: var(--sh-text-muted);
  --bs-tertiary-color: var(--sh-text-faint);
  --bs-emphasis-color: var(--sh-heading);
  --bs-heading-color: var(--sh-heading);
  --bs-border-color: var(--sh-border);
  --bs-link-color: var(--sh-accent-strong);
}
body .modal-content,
body .dropdown-menu,
body .offcanvas,
body .list-group-item,
body .card { color: var(--sh-text); }
body .text-muted,
body .form-text { color: var(--sh-text-muted) !important; }
body .text-dark,
body .text-black,
body .text-body { color: var(--sh-text) !important; }
body .fw-semibold, body .fw-bold { color: inherit; }
:root {
  --bs-tertiary-bg: var(--sh-surface-sunken);
  --bs-secondary-bg: var(--sh-surface-2);
}
body .input-group-text {
  background: var(--sh-surface-sunken);
  color: var(--sh-text-muted);
  border-color: var(--sh-border-strong);
}

/* The scrollbar's lane is kept even while a page is still short. Without it,
   a list that loads a moment later (update page, history) makes the scrollbar
   appear and the whole layout jumps sideways by its width. */
html { scrollbar-gutter: stable; }
