/* The widget engine's own stylesheet — the modal, its form controls and the trigger
   button reset. It ships separately from site.css because the engine is used by pages
   the platform does not render: /static/js/widget.js and this file are injected into a
   custom-HTML page together, so a page that styles itself still gets a working modal.
   Before this split the modal's rules lived in site.css, which only the skeleton
   linked, so a custom page could open an invisible (opacity: 0) modal.

   Note for anyone editing the trigger rules: widget.js hides a trigger that is not in
   the server's enabled list by setting the `hidden` attribute on it. That is only
   cosmetic on its own — any `display` rule in the page's own CSS outranks the UA
   stylesheet's `[hidden]` — so the rule below is deliberately `!important` and is the
   one that actually removes it. */

/* Fail-closed trigger visibility. The engine marks every trigger it is willing to open
   with `data-nx-ready`; until then the trigger is not shown at all. Two consequences,
   both intended: a widget that is switched off in the admin panel never flashes into
   view before the script runs, and a page whose widget.js failed to load shows no dead
   buttons. The markup can therefore keep every `data-widget` hook for later.

   `data-widget-reviews` is the boolean spelling of the same hook — a hand-written page
   may mark a review block with the attribute alone instead of `data-widget="reviews"` —
   so it is covered by the same rule, for the same reason. */
[data-widget]:not([data-nx-ready]),
[data-widget-reviews]:not([data-nx-ready]) { display: none !important; }

/* The reset a trigger styled by the page may not inherit. */
[data-widget] { cursor: pointer; }
[data-widget-reviews] { cursor: pointer; }

.nx-overlay {
  position: fixed; inset: 0; z-index: 900;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(4, 8, 20, .68);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  opacity: 0; transition: opacity .18s ease;
  overflow-y: auto;
}
.nx-overlay.is-open { opacity: 1; }

.nx-dialog {
  width: 100%; max-width: 460px; margin: auto;
  padding: 24px; border-radius: 18px;
  background: var(--bg-surface, #0B1329);
  border: 1px solid rgba(58, 134, 255, .28);
  box-shadow: 0 30px 70px rgba(2, 6, 18, .6);
  transform: translateY(14px) scale(.97);
  transition: transform .22s cubic-bezier(.2, .8, .3, 1);
}
.nx-overlay.is-open .nx-dialog { transform: translateY(0) scale(1); }

/* The header is one flex row: the title keeps the left edge and the close button is
   pushed to the far right, on the title's own line, at every title length. `align-items`
   is centre rather than flex-start because the × is a fixed 34px control and the title
   can wrap — centring keeps the two on one visual line instead of leaving the × floating
   above a two-line heading. */
.nx-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-bottom: 16px;
}
.nx-title { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 1.15rem; color: var(--text-main, #F1F5F9); }
.nx-close {
  flex: 0 0 auto; align-self: flex-start;
  width: 34px; height: 34px; margin: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 9px; border: 1px solid var(--border-active, rgba(255, 255, 255, .14));
  background: var(--overlay-weak, rgba(255, 255, 255, .05)); color: var(--text-muted, #94A3B8);
  font-family: inherit; font-size: 20px; line-height: 1; cursor: pointer;
}
.nx-close:hover { color: var(--text-main, #F1F5F9); border-color: var(--accent-a); }
.nx-close:focus-visible { outline: 2px solid var(--accent-a); outline-offset: 2px; }

.nx-demo {
  margin: 0 0 16px; padding: 10px 13px; border-radius: 10px;
  background: rgba(58, 134, 255, .12); border: 1px solid rgba(58, 134, 255, .3);
  font-size: 13px; color: var(--text-muted, #94A3B8);
}

.nx-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.nx-field.is-full { grid-column: 1 / -1; }
.nx-field label { font-size: 13px; font-weight: 600; color: var(--text-muted, #94A3B8); }
/* The field's colours come from the palette tokens first (emitted per layout by
   _site_head.html), then from the dark fallbacks. `color-scheme` is stated alongside
   them because the engine is also injected into custom-HTML pages, which may link this
   file and nothing else: it tells the browser to paint the native date/time controls,
   the select popup and the scrollbars to match the field rather than to guess. */
.nx-field input, .nx-field select, .nx-field textarea {
  width: 100%; padding: 11px 13px; border-radius: 10px;
  border: 1px solid var(--input-border, var(--border-active, rgba(255, 255, 255, .14)));
  background: var(--input-bg, var(--surface-input, #0A1226)); color: var(--input-text, var(--text-main, #F1F5F9));
  font-family: inherit; font-size: 14.5px;
  color-scheme: dark;
}
/* A light palette flips the field to white with near-black text. The palette states
   `input_bg` for exactly this, and `_site_head.html` puts the palette on the body, so
   the rule can be a document-level fact instead of a per-control guess. */
body[data-surface="light"] .nx-field input,
body[data-surface="light"] .nx-field select,
body[data-surface="light"] .nx-field textarea,
body[data-surface="light"] .nx-dialog input,
body[data-surface="light"] .nx-dialog select,
body[data-surface="light"] .nx-dialog textarea {
  background: #FFFFFF; color: #1A1A1A; border: 1px solid #E2E8F0;
  color-scheme: light;
}
body[data-surface="dark"] .nx-field input,
body[data-surface="dark"] .nx-field select,
body[data-surface="dark"] .nx-field textarea { color-scheme: dark; }
.nx-field input::placeholder, .nx-field textarea::placeholder { color: var(--text-dim, #64748B); opacity: 1; }
.nx-field input:focus, .nx-field select:focus, .nx-field textarea:focus {
  outline: none; border-color: var(--accent-a);
  box-shadow: 0 0 0 3px rgba(58, 134, 255, .18);
}
.nx-estimate {
  display: flex; align-items: center; justify-content: space-between;
  margin: 4px 0 14px; padding: 12px 15px; border-radius: 10px;
  background: rgba(58, 134, 255, .1); border: 1px solid rgba(58, 134, 255, .26);
  font-size: 14px; color: var(--text-muted, #94A3B8);
}
.nx-estimate span:last-child { font-weight: 800; color: var(--text-main, #F1F5F9); }

.nx-basket { display: grid; gap: 8px; margin-bottom: 14px; }
.nx-basket-row {
  display: grid; grid-template-columns: auto 1fr auto auto; gap: 10px; align-items: center;
  padding: 9px 11px; border-radius: 10px;
  background: var(--surface-input, rgba(6, 11, 25, .5)); border: 1px solid var(--border-subtle, rgba(255, 255, 255, .1));
}
.nx-basket-name { font-size: 14px; }
.nx-basket-price { font-size: 13px; color: var(--accent-a); font-weight: 700; white-space: nowrap; }
.nx-basket-row input[type=number] { width: 66px; padding: 7px 8px; }
.nx-note { color: var(--text-dim, #64748B); font-size: 13px; margin: 0; }

.nx-submit {
  width: 100%; margin-top: 6px; padding: 13px 18px; border: 0; border-radius: 11px;
  background: linear-gradient(135deg, var(--accent-a), var(--accent-b, #3A86FF));
  color: var(--on-accent, #fff); font-family: inherit; font-size: 15px; font-weight: 800; cursor: pointer;
}
.nx-submit:disabled { opacity: .6; cursor: progress; }

.nx-status { min-height: 18px; margin-top: 10px; font-size: 13px; }
.nx-status.is-ok { color: #6EE7B7; }
.nx-status.is-error { color: #FCA5A5; }

/* Honeypot: off-screen rather than display:none, because some bots skip
   fields that are not rendered at all. */
.nx-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Fallback triggers for a custom-HTML page using <div id="nexilis-widgets">. */
.nx-launcher {
  display: inline-flex; align-items: center; margin: 0 8px 8px 0;
  padding: 11px 17px; border-radius: 11px; cursor: pointer;
  border: 1px solid var(--accent-line, rgba(58, 134, 255, .34)); background: var(--accent-soft, rgba(58, 134, 255, .12));
  color: var(--text-main, #F1F5F9); font-family: inherit; font-size: 14px; font-weight: 700;
}
.nx-launcher:hover { background: var(--accent-soft, rgba(58, 134, 255, .22)); }

@media (max-width: 520px) {
  .nx-contacts { grid-template-columns: 1fr; }
  .nx-dialog { padding: 20px; border-radius: 16px; }
}

/* ==========================================================================
   The floating trigger (general widget engine, trigger mode 1 of 2)

   One of these is rendered per *configured* widget, and the engine marks it ready only
   once it has accepted the widget — the same fail-closed rule as every other trigger.
   An unconfigured widget therefore never leaves a dead button behind.

   The buttons are positioned individually inside one fixed, full-viewport layer rather
   than laid out in a flex row: that way a corner is a property of each button, several
   buttons stack along that same edge, and no `:has()` selector is needed to move the
   container around.
   ========================================================================== */

.nx-floating {
  position: fixed; inset: 0; z-index: 780;
  pointer-events: none;
}
.nx-floating > * { pointer-events: auto; }

/* An empty pinned container is dead markup — a full-screen layer with nothing in it,
   which would still swallow the page's own clicks. The container itself is never marked
   ready (only the buttons are) and it holds nothing else, so "no ready button inside"
   is the same statement as "nothing to show". `:has` is in every engine this panel
   supports; where it is not understood the rule does not apply and the layer stays
   click-through, which the `pointer-events` rules above already guarantee. */
.nx-floating:not(:has([data-nx-ready])) { display: none; }

.nx-fab {
  position: absolute;
  display: inline-flex; align-items: center; gap: 9px;
  min-height: 54px; padding: 0 20px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--accent-line, rgba(58, 134, 255, .4));
  background: linear-gradient(135deg, var(--accent-a, #3A86FF), var(--accent-b, #22D3EE));
  color: var(--on-accent, #fff);
  font-family: inherit; font-size: 15px; font-weight: 800;
  box-shadow: 0 18px 40px -14px rgba(0, 0, 0, .65);
  transition: transform .18s ease, filter .18s ease;
}
.nx-fab:hover { transform: translateY(-2px); filter: brightness(1.06); }
.nx-fab:focus-visible { outline: 2px solid var(--accent-a, #3A86FF); outline-offset: 3px; }
.nx-fab .material-symbols-outlined { font-size: 24px; }

/* The four placements the admin panel offers, plus the two centred variants. The
   offsets differ so stacked buttons never overlap: the first sits on the edge, and
   each later one is pushed a further 66px along the stacking axis. */
.nx-fab-bottom-right { right: 22px; bottom: 22px; }
.nx-fab-bottom-right ~ .nx-fab-bottom-right { bottom: 88px; }
.nx-fab-bottom-right ~ .nx-fab-bottom-right ~ .nx-fab-bottom-right { bottom: 154px; }
.nx-fab-bottom-left { left: 22px; bottom: 22px; }
.nx-fab-bottom-left ~ .nx-fab-bottom-left { bottom: 88px; }
.nx-fab-bottom-left ~ .nx-fab-bottom-left ~ .nx-fab-bottom-left { bottom: 154px; }
.nx-fab-top-right { right: 22px; top: 84px; }
.nx-fab-top-left { left: 22px; top: 84px; }
.nx-fab-bottom-center { left: 50%; transform: translateX(-50%); bottom: 22px; }
.nx-fab-bottom-center:hover { transform: translateX(-50%) translateY(-2px); }

/* ---------- Embedded mode ----------
   The same form the dialog holds, rendered as a block of the page. It is deliberately
   plain: a page that puts a calculator in its own section already has a background, a
   width and a heading style, and the engine should add as little as possible on top of
   that. What it does add is the title line (the dialog header's job, elsewhere) and the
   colour tokens the controls already use. */
.nx-embed {
  display: block;
  padding: 20px 22px;
  border-radius: var(--radius-lg, 16px);
  background: var(--surface-card, rgba(6, 11, 25, .45));
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, .1));
}
.nx-embed-title {
  margin: 0 0 14px; font-size: 18px; font-weight: 800;
  color: var(--text-main, #F1F5F9);
}
/* The form inside an embed is a column of fields with no extra box of its own: the
   container above is the box. */
.nx-embed .nx-form { display: block; }
.nx-embed .nx-status { margin-bottom: 0; }
/* A read-only reviews list has no send button, so it needs no reserved space for one. */
.nx-embed .nx-submit[hidden] { display: none; }

body[data-surface="light"] .nx-embed { background: #FFFFFF; border-color: #E2E8F0; }
body[data-surface="light"] .nx-embed-title { color: #0F172A; }

/* ==========================================================================
   Config-driven form pieces

   The six builders in widget.js emit these from whatever the admin panel stored:
   a booking's slot select, a calculator's modifiers, a quiz's multi-select groups,
   a basket's product rows and a review list. None of it is widget-type-specific CSS;
   a new widget type reuses these blocks.
   ========================================================================== */

.nx-intro { margin-bottom: 14px; }
/* The modal's own <h2> is the widget's title; the intro block carries the subtitle only,
   which is why there is no `.nx-intro-title` rule any more — the duplicated heading it
   used to style was the bug. */
.nx-intro-sub { margin: 0; font-size: 13px; color: var(--text-muted, #94A3B8); }

.nx-switch { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 600; }
.nx-switch input { width: auto; }

/* ---------- Calculator range options ----------
   A slider carries its own min/max/step, so the native control is left alone; the
   readout above it is what makes the position legible ("3 авто (+3 600 ₴)"). */
.nx-range { gap: 8px; }
.nx-range-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.nx-range-title { font-size: 13px; font-weight: 600; color: var(--text-muted, #94A3B8); }
.nx-range-value { font-size: 13px; font-weight: 800; color: var(--accent-a); white-space: nowrap; }
.nx-range input[type="range"] {
  width: 100%; padding: 0; margin: 2px 0 0; border: 0; background: transparent;
  accent-color: var(--accent-a);
}
/* A light palette needs its own track/thumb contrast; `accent-color` follows, and the
   native control keeps its own background either way. */
body[data-surface="light"] .nx-range input[type="range"] { color-scheme: light; }

.nx-multi-list { display: grid; gap: 8px; }
.nx-check { display: flex; align-items: center; gap: 9px; font-size: 14px; font-weight: 500; }
.nx-check input { width: auto; }

/* ---------- Day picker (booking) ----------
   The booking form asks for the day before it offers a time, and the days it offers are
   only the ones the admin marked as working days. The chips are radio buttons wrapped
   in labels, so the whole picker is keyboard-navigable and needs no JavaScript beyond
   what builds it. A day with no free slots is rendered disabled rather than left out:
   the visitor can see that the day exists and is full. */
.nx-days {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(62px, 1fr));
  gap: 8px; max-height: 208px; overflow-y: auto; padding: 2px;
}
.nx-day { position: relative; display: block; cursor: pointer; }
/* The radio itself stays reachable for screen readers and the keyboard, but the chip is
   what the visitor sees and clicks. */
.nx-day-input {
  position: absolute; width: 1px; height: 1px; margin: 0; padding: 0;
  opacity: 0; pointer-events: none;
}
.nx-day-face {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 8px 4px; border-radius: 10px; text-align: center;
  background: var(--overlay-weak, rgba(255, 255, 255, .05));
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, .1));
  transition: border-color .15s ease, background .15s ease;
}
.nx-day:hover .nx-day-face { border-color: var(--accent-a); }
.nx-day-input:focus-visible + .nx-day-face { outline: 2px solid var(--accent-a); outline-offset: 2px; }
.nx-day-input:checked + .nx-day-face {
  background: var(--accent-soft, rgba(58, 134, 255, .12));
  border-color: var(--accent-a);
}
.nx-day-dow { font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--text-dim, #64748B); }
.nx-day-date { font-size: 13.5px; font-weight: 700; white-space: nowrap; }
/* A closed day is shown, not hidden: the visitor sees that the day exists and why it
   cannot be booked. It is `is-off` and the radio inside it is disabled, so the browser
   refuses to submit it as well as the chip refusing to be clicked. */
.nx-day.is-off .nx-day-face { opacity: .4; }
.nx-day.is-off { cursor: not-allowed; }
.nx-day.is-off .nx-day-face { border-style: dashed; }
.nx-day-badge {
  margin-top: 2px; padding: 1px 6px; border-radius: 999px;
  font-size: 9.5px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-dim, #64748B);
  background: var(--overlay-weak, rgba(255, 255, 255, .06));
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, .1));
  white-space: nowrap;
}
.nx-days-hint { margin: 6px 0 0; font-size: 12.5px; color: var(--text-dim, #64748B); }

/* ---------- Multi-step forms (calculator) ----------
   The calculation is one panel and the contact details another; only `hidden` changes.
   `[hidden]` is forced here because the panels are laid out with `display: grid`, which
   would otherwise beat the attribute's own `display: none`. */
.nx-step[hidden] { display: none !important; }
.nx-step { display: grid; gap: 12px; }
/* The contact block is a two-column grid inside whichever step holds it — the name and
   the phone side by side, a comment underneath — because it is a grid *within* a panel
   now, and a `.nx-row` beside it would only wrap one child. */
.nx-contacts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.nx-contacts .nx-field.is-full { grid-column: 1 / -1; }
.nx-summary {
  padding: 10px 13px; border-radius: 10px; font-size: 13px; line-height: 1.5;
  background: var(--overlay-weak, rgba(255, 255, 255, .05));
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, .1));
  color: var(--text-muted, #94A3B8);
}
.nx-summary:empty { display: none; }
.nx-step-nav { display: flex; justify-content: flex-start; }
.nx-step-back {
  padding: 9px 14px; border-radius: 9px; cursor: pointer;
  background: transparent; color: var(--text-muted, #94A3B8);
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, .1));
  font-family: inherit; font-size: 13.5px; font-weight: 700;
}
.nx-step-back:hover { border-color: var(--accent-a); color: var(--text-main, #F1F5F9); }

.nx-basket-cat {
  margin: 12px 0 4px; font-size: 12px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; color: var(--text-dim, #64748B);
}
.nx-basket-row { grid-template-columns: auto auto 1fr auto auto; }
.nx-basket-thumb { display: block; width: 40px; height: 40px; border-radius: 9px; overflow: hidden; background: var(--overlay-weak, rgba(255, 255, 255, .05)); }
.nx-basket-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.nx-basket-body { display: flex; flex-direction: column; gap: 3px; cursor: pointer; }
.nx-basket-desc { font-size: 12.5px; color: var(--text-muted, #94A3B8); }
.nx-badges { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 3px; }
.nx-badge {
  padding: 2px 7px; border-radius: 999px; font-size: 11px; font-weight: 700;
  background: var(--accent-soft, rgba(58, 134, 255, .12));
  border: 1px solid var(--accent-line, rgba(58, 134, 255, .3));
  color: var(--text-main, #F1F5F9);
}

.nx-reviews { display: grid; gap: 12px; margin: 0 0 16px; padding: 0; list-style: none; }
.nx-review {
  padding: 12px 14px; border-radius: 12px;
  background: var(--surface-strong, rgba(255, 255, 255, .03));
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, .09));
}
.nx-review-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.nx-review-author { font-size: 14px; }
.nx-review-rating { color: #F59E0B; letter-spacing: 1px; font-size: 13px; }
.nx-review-text { margin: 6px 0 0; font-size: 13.5px; color: var(--text-muted, #94A3B8); }
.nx-review-date { display: block; margin-top: 6px; font-size: 12px; color: var(--text-dim, #64748B); }
