/* panels.css — floating panel layer, module bar, connection widget,
 * adapted from ostnoctua's .on-glass/.on-modbar/.on-conn-cluster/.on-mod/
 * .on-float (ostnoctua.css) onto UI3's own theme.css tokens. See
 * js/panels.js for the panel drag/resize/minimize behavior this styles.
 *
 * .glass is ostnoctua's actual "frosted" recipe (translucent background +
 * backdrop-filter blur + subtle border + soft shadow) — the one visual
 * ingredient an earlier, flat-opaque-box version of this file was missing
 * entirely (Gilles, 2026-09-25: "c'est tout moisi ... regarde comment
 * c'est fait sur ostnoctua"). Every floating surface in UI3 uses it. */
.glass {
  background: var(--bg-panel);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
}

/* ------------------------------------------------------------ module bar
 * One glass pill floating top-left over the canvas (ostnoctua's
 * "on-glass on-modbar" nav) — not a boxed-off full-width strip. */
#modbar {
  position: fixed; top: 12px; left: 12px; z-index: 20;
  display: flex; align-items: center; gap: 6px;
  padding: 8px;
  max-width: calc(100vw - 24px);
  overflow-x: auto; overflow-y: hidden;
}
/* Generic divider, shared by any glass bar that groups buttons into zones
   (currently just #quicknav's nav/actions split — see below). */
.bar-sep { width: 1px; align-self: stretch; margin: 2px 4px; background: var(--border); flex-shrink: 0; }
/* Exactly ostnoctua's .on-mod recipe (ostnoctua/web/ostnoctua.css): fixed
   56x56 square, icon above a small label, not a variable-width inline
   pill — Gilles, 2026-09-25, after an inline-pill attempt: "reprend
   exactement le principe de ostnoctua (taille fixe, libellé en dessous
   police reduite)". */
.mod-btn {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; width: 56px; height: 56px; flex-shrink: 0;
  background: var(--accent-faint); color: var(--text-muted);
  border: 1px solid var(--border-subtle); border-radius: var(--radius);
  cursor: pointer; font: inherit;
  transition: border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
/* Global-state dot (Gilles, 2026-09-27) — same .status-dot component as the
   module's own "signals" property, just relocated to the button's corner.
   Absent (.hidden) for modules that never declare a "state" element. */
.mod-btn-dot { position: absolute; top: 5px; right: 5px; margin-right: 0; }
.mod-btn:hover { color: var(--text-secondary); border-color: var(--accent-dim); }
.mod-btn:hover .material-icons { transform: scale(1.15); }
.mod-btn.active {
  background: var(--accent-subtle); color: var(--accent); border-color: var(--accent);
  box-shadow: var(--shadow-glow);
}
.mod-btn .material-icons { font-size: 22px; transition: transform 0.2s; }
.mod-btn-label {
  font-size: 9px; letter-spacing: 0.5px; max-width: 52px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* --------------------------------------------------------------- quicknav
 * Per-module bottom bar, one glass pill like #modbar — Gilles, 2026-09-27:
 * generic across every module. Two zones: navigation (left — one button per
 * panel/level1, filled by render.js's setQuickNavRoot()/views.js's
 * renderQuickNav()) and actions (right — reorganize/params, this same
 * module's own controls, moved out of #top-right since "le bouton params
 * ... se rapporte au module en cours d'affichage"). The whole bar is hidden
 * for non-module views (docked screens have neither); the nav zone alone
 * (plus its separator) additionally hides itself when a module has no
 * panels to jump to, since the actions zone still matters then. */
#quicknav {
  position: fixed; bottom: 12px; left: 50%; transform: translateX(-50%); z-index: 20;
  display: flex; align-items: center; gap: 6px;
  padding: 8px;
  max-width: calc(100vw - 24px);
  overflow-x: auto;
}
.quicknav-zone { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
/* The reorganize/params chips no longer carry their own standalone .glass
   background (they sit inside #quicknav's shared pill instead) but keep a
   faint background/border of their own so they still read as buttons —
   same scoping convention as #modbar's own .mod-btn. */
#quicknav-actions .icon-chip { background: var(--accent-faint); border: 1px solid var(--border-subtle); border-radius: var(--radius); }
.quicknav-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 36px; padding: 0 12px; flex-shrink: 0;
  background: var(--accent-faint); color: var(--text-secondary);
  border: 1px solid var(--border-subtle); border-radius: var(--radius);
  cursor: pointer; font: inherit; font-size: 12px; white-space: nowrap;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.quicknav-btn .material-icons { font-size: 16px; }
.quicknav-btn:hover { color: var(--accent); border-color: var(--accent-dim); }
.quicknav-btn.active {
  background: var(--accent-subtle); color: var(--accent); border-color: var(--accent);
  box-shadow: var(--shadow-glow);
}

/* ------------------------------------------------------------ top-right
 * Theme toggle + connection widget, each its own glass chip, floating at
 * the same level as #modbar (Gilles, 2026-09-25: "tout au meme niveau, un
 * seul conteneur commun"). The theme toggle is deliberately its own
 * always-visible chip, never nested inside the collapsible connection
 * panel — burying it there once meant Gilles genuinely couldn't find it. */
#top-right { position: fixed; top: 12px; right: 12px; z-index: 20; display: flex; align-items: flex-start; gap: 8px; }

/* UI zoom (font size + everything else, incl. floating panels) — Gilles,
   2026-09-28: "ajuster la taille de police de tous les composants ... la
   taille des fenetres doit s'ajuster en consequence"; 2026-09-29:
   "remplace les boutons +/- par un slider qui s'affiche quand on clic sur
   un bouton unique". One icon chip (like the theme toggle); clicking it
   folds out a small glass popover with the slider, same collapsible
   pattern as #conn-widget. Applied via CSS `zoom` on <body> (app.js)
   rather than a transform: zoom is a real browser-level scale, so
   panels.js's own drag/resize math (getBoundingClientRect/clientX/
   clientY) keeps working unmodified, and position:fixed chrome (this very
   bar included) stays anchored to the real viewport instead of being
   caught in a transformed containing block. */
/* The toggle chip itself scales with everything else, like every other
   #top-right chip — no exception there (Gilles, 2026-09-29: "le bouton Tt
   doit se comporter comme les autres"). Only the popup is the exception:
   position:fixed (its own app.js-computed top/left, in real viewport px —
   taken out of #top-right's flex flow entirely, so it can no longer shift
   position as its own size changes) + zoom: calc(1/--ui-zoom) (app.js sets
   --ui-zoom to the same value as body's own zoom) to counter that
   inherited zoom back out to an effective 1x, constant size regardless of
   the level currently set — the first attempt put both the button and the
   popup in one zoom-countering wrapper, which shifted the whole thing
   around inside the flex row as its box's own contribution to that row's
   layout changed size ("le slide ne devrait pas changer de taille" turned
   into "la ca change de position aussi"). */
#zoom-widget { position: relative; flex-shrink: 0; }
.zoom-panel {
  position: fixed; z-index: 30; padding: 8px 12px; display: flex; align-items: center;
  zoom: calc(1 / var(--ui-zoom, 1));
}
/* .hidden (base.css, !important) toggles this panel's visibility. */
#zoom-slider { width: 110px; accent-color: var(--accent); cursor: pointer; }

.icon-chip {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex-shrink: 0;
  color: var(--text-secondary); cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.icon-chip:hover { color: var(--accent); border-color: var(--accent-dim); }
.icon-chip .material-icons { font-size: 19px; }
.icon-chip:disabled { color: var(--text-dim); cursor: not-allowed; opacity: 0.5; }
.icon-chip.active { color: var(--accent); border-color: var(--accent); box-shadow: var(--shadow-glow); }
/* Unsaved profile changes (module.profile.changed) — params gear button. */
.icon-chip.has-changes::after {
  content: ""; position: absolute; top: 3px; right: 3px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--error); box-shadow: 0 0 4px rgba(var(--error-rgb), 0.7);
}

/* ------------------------------------------------------- connection widget
 * Collapsed to a small status pill once connected; click anywhere on it to
 * fold out the full settings (brand/URL/media URL/language/connect). */
#conn-widget { position: relative; flex-shrink: 0; }
.conn-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 0 12px; height: 36px;
  color: var(--text-secondary); cursor: pointer; font: inherit; font-size: 12px;
}
.conn-toggle:hover { border-color: var(--accent-dim); }
.conn-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--standby); }
.conn-dot.connected { background: var(--ok); box-shadow: 0 0 6px rgba(var(--ok-rgb), 0.7); }
.conn-dot.connecting { background: var(--busy); animation: pulse-dot 1s ease-in-out infinite; }
.conn-dot.disconnected { background: var(--error); }
.conn-caret { font-size: 16px !important; color: var(--text-dim); transition: transform 0.15s ease; }
#conn-widget.open .conn-caret { transform: rotate(180deg); }
.conn-panel {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 30;
  display: flex; flex-direction: column; gap: 8px;
  width: 280px; padding: var(--space-3);
}
/* .hidden (base.css, !important) toggles this panel's visibility. */

/* Controller/Files/Logs — this client's own chrome, not OST modules, so
   they live here as a small settings-style list instead of in #modbar
   (Gilles, 2026-09-25). */
.settings-nav { display: flex; flex-direction: column; gap: 1px; margin: 0 calc(var(--space-3) * -1); }
.settings-nav-item {
  display: flex; align-items: center; gap: 8px;
  padding: 7px var(--space-3);
  background: none; border: none; border-radius: 0;
  color: var(--text-secondary); cursor: pointer; font: inherit; font-size: 12.5px; text-align: left;
  transition: background 0.15s ease, color 0.15s ease;
}
.settings-nav-item:hover { background: var(--bg-hover); color: var(--text); }
.settings-nav-item.active { background: var(--accent-subtle); color: var(--accent-strong); font-weight: 600; }
.settings-nav-item .material-icons { font-size: 16px; color: var(--text-muted); flex-shrink: 0; }
.settings-nav-item.active .material-icons { color: var(--accent); }
.settings-sep { height: 1px; background: var(--border); margin: 2px 0; }

/* ------------------------------------------------------------- canvas ---
 * Fills #app edge to edge (#modbar/#top-right float on top of it, see
 * above). Hosts either #panel-layer (floating panels, one module at a
 * time) or #docked-view (Controller/Files/Logs/connect screen), toggled
 * via .hidden — see js/app.js. */
#canvas { position: absolute; inset: 0; background: var(--bg); }
/* Full-viewport background image (a template's own module.image, e.g. the
   guider template's guide frame) — sits behind #panel-layer/#docked-view
   (DOM order, both position:absolute with no z-index of their own, so
   later siblings paint on top), letterboxed (resized, aspect ratio kept —
   never stretched or cropped), click opens the fullscreen viewer. Ported
   from ostnoctua's own #on-bg/#on-bg-img (Gilles, 2026-09-29: "affiche
   l'image derriere en ecran de fond, comme dans ostnoctua") — see
   js/imagelayer.js. Fixed near-black regardless of theme, same as
   ostnoctua: astro imagery reads wrong against a light theme's own
   background. */
#bg-layer { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
#bg-layer.img-active { background: #05070c; }
#bg-image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; image-rendering: pixelated; opacity: 0.92; cursor: zoom-in; }
/* Sky-dome view (templates/navigator.js, js/sky-engine.js) — same shared
   #bg-layer, a <canvas> instead of an <img>. Deep space black regardless
   of theme, same reasoning as .img-active. */
#bg-layer.sky-active { background: #05070c; }
#sky-canvas { position: absolute; inset: 0; width: 100%; height: 100%; cursor: grab; }
#sky-canvas.dragging { cursor: grabbing; }
#panel-layer, #docked-view { position: absolute; inset: 0; overflow: auto; }
#docked-view { padding: var(--space-5); }
#docked-view > * { max-width: 1180px; margin: 0 auto; }
#panel-layer.hidden, #docked-view.hidden { display: none; }
/* #panel-layer fills the whole canvas even where no panel actually sits,
   so by default it would swallow every pointer event aimed at empty space
   before it could reach #bg-layer's own content underneath (the image
   viewer's click, the sky dome's wheel-zoom/drag — see js/sky-engine.js) —
   caught live, 2026-09-29: "le clic sur l'image de fond ne marche pas",
   then the same root cause again for the sky dome. Fixed generically
   rather than forwarding one event type at a time: the layer itself is
   click-through, only actual panels opt back into being interactive.
   pointer-events:none does mean #panel-layer's own wheel-scroll no longer
   works when hovering genuinely empty space - not a real loss here, since
   the established way to reach a panel stacked below the fold is already
   its quicknav button (panels.js's togglePanel() calls scrollIntoView()),
   not manually scrolling the layer. */
#panel-layer { pointer-events: none; }
.panel-float { pointer-events: auto; }

/* ------------------------------------------------------- fullscreen image
 * viewer (js/imagelayer.js's openImageViewer()) — wheel zooms toward the
 * cursor, drag pans, Esc/backdrop/close button closes. Same behavior and
 * class shapes as ostnoctua's .oi-viewer/.oi-stage/.oi-img/.oi-close. */
.img-viewer { position: fixed; inset: 0; z-index: 9999; background: rgba(2, 3, 8, 0.93); backdrop-filter: blur(3px); }
.img-viewer-stage { position: absolute; inset: 0; overflow: hidden; cursor: grab; }
.img-viewer-stage.grabbing { cursor: grabbing; }
.img-viewer-img { position: absolute; top: 0; left: 0; transform-origin: 0 0; image-rendering: pixelated; user-select: none; -webkit-user-drag: none; }
.img-viewer-close {
  position: absolute; top: 14px; right: 18px;
  width: 34px; height: 34px; border-radius: var(--radius);
  font-size: 22px; line-height: 1; cursor: pointer;
  border: 1px solid var(--border); background: var(--bg-panel); color: var(--text);
}
.img-viewer-close:hover { border-color: var(--accent-dim); }

.panel-float {
  position: absolute;
  min-width: 200px; max-width: 92%;
  display: flex; flex-direction: column;
  background: var(--bg-panel-solid);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
}
.panel-float:hover { border-color: var(--border-strong); }
.panel-float-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 6px 6px 10px;
  cursor: grab; user-select: none;
  border-bottom: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
.panel-float.dragging { opacity: 0.92; box-shadow: var(--shadow-pop); }
.panel-float.dragging .panel-float-bar { cursor: grabbing; }
/* Brief highlight when a quicknav button brings this panel forward. */
.panel-float.flash { animation: panel-flash 0.9s ease; }
@keyframes panel-flash {
  0%, 100% { box-shadow: var(--shadow-panel); }
  25% { box-shadow: 0 0 0 3px var(--accent-subtle), 0 0 24px rgba(var(--accent-rgb), 0.35); }
}
.panel-float-grip { opacity: 0.3; letter-spacing: -3px; font-size: 12px; color: var(--text-muted); }
.panel-float-title {
  flex: 1; font-size: 11px; font-weight: 700; letter-spacing: 0.5px;
  text-transform: uppercase; color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.panel-float-min {
  border: 0; background: transparent; color: var(--text-muted);
  cursor: pointer; font-size: 16px; line-height: 1; padding: 0 4px; opacity: 0.7;
}
.panel-float-min:hover { opacity: 1; color: var(--accent); }
.panel-float-body { padding: var(--space-3) var(--space-4); overflow: auto; max-height: 74vh; }
.panel-float.collapsed .panel-float-body { display: none; }
.panel-float.collapsed .panel-float-bar { border-bottom: 0; }

.panel-float-resize {
  position: absolute; right: 3px; bottom: 3px;
  width: 15px; height: 15px; cursor: nwse-resize; opacity: 0.4;
  background:
    linear-gradient(135deg, transparent 44%, var(--accent) 44% 56%, transparent 56%),
    linear-gradient(135deg, transparent 66%, var(--accent) 66% 80%, transparent 80%);
}
.panel-float-resize:hover { opacity: 0.9; }
.panel-float.collapsed .panel-float-resize { display: none; }
.panel-float.resizing { user-select: none; }
.panel-float.resized { max-width: none; }
.panel-float.resized .panel-float-body { flex: 1 1 auto; min-height: 0; max-height: none; }

.hint-card {
  position: absolute; left: var(--space-5); top: var(--space-5);
  padding: 14px 18px; opacity: 0.75; max-width: 340px;
}
