/* appearance.css — the background layer stack, the Vidro style extras and the
   Appearance section of Ajustes. Loaded last so its overrides land on top. */

/* ===========================================================================
   Background layer stack
   ---------------------------------------------------------------------------
   Never painted on <body>: a background there propagates to the canvas, which
   sits under everything and would show through instead of these layers. This
   stack is fixed, isolated and inert, so the animated layers composite on their
   own without dragging the app's layout or paint into every frame.
   =========================================================================== */
.bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  contain: strict;

  /* Symmetric ease-in-out: over a minute-long cycle it reads as breathing.
     `linear` looks like a screensaver, `ease-out` reads as a stutter. */
  --drift-ease: cubic-bezier(0.45, 0, 0.55, 1);
}

/* Durations are deliberately non-proportional. 30/60/90 realign every 90s and
   the eye catches the loop; 79/97/113 are coprime and only realign after days. */
.bg[data-bg-speed="slow"]   { --drift-a: 79s; --drift-b: 97s;  --drift-c: 113s; }
.bg[data-bg-speed="medium"] { --drift-a: 41s; --drift-b: 53s;  --drift-c: 67s; }

/* --- Base: the flat colour or the gradient -------------------------------- */
.bg__base { position: absolute; inset: 0; }
.bg[data-bg-mode="system"]            .bg__base { background: var(--bg-gradient); }
.bg[data-bg-mode="solid"]             .bg__base { background: hsl(var(--bg-c1)); }
.bg[data-bg-mode="gradient_static"]   .bg__base { background: linear-gradient(var(--bg-angle), var(--bg-stops)); }
.bg[data-bg-mode="gradient_animated"] .bg__base { background: linear-gradient(var(--bg-angle), var(--bg-stops)); }

/* --- Orbs: the movement ---------------------------------------------------
   The blur is applied once and never animated: the blurred layer rasterises a
   single time and the GPU only translates it afterwards. Animating the filter
   (or background-position) would repaint full-screen every frame, and under the
   Vidro style each repaint forces every panel's backdrop-filter to recompute. */
.bg__orb {
  position: absolute;
  width: 62vmax;
  height: 62vmax;
  border-radius: 50%;
  filter: blur(var(--orb-blur, 90px));
  opacity: 0;
  transition: opacity 320ms var(--ease), background-color 3s linear;
}
/* will-change reserves video memory for as long as it is set, so it exists only
   while something is actually moving. */
.bg[data-bg-mode="gradient_animated"] .bg__orb {
  opacity: var(--bg-orb-opacity);
  will-change: transform;
}

/* The transform on each orb is its animation's 33% frame, not the origin: when
   the animation is removed (speed "parada" or reduced motion) the orbs rest in
   a composition that was chosen to look good rather than wherever 0% happens
   to put them. */
.bg__orb--a {
  background-color: hsl(var(--bg-c1));
  top: -18%; left: -12%;
  transform: translate3d(12vw, 8vh, 0) scale(1.08);
}
.bg__orb--b {
  background-color: hsl(var(--bg-c2));
  top: 32%; left: 48%;
  transform: translate3d(-14vw, -6vh, 0) scale(0.94);
}
.bg__orb--c {
  background-color: hsl(var(--bg-c3));
  top: 58%; left: 6%;
  transform: translate3d(7vw, -12vh, 0) scale(1.05);
}

.bg[data-bg-mode="gradient_animated"]:not([data-bg-speed="off"]) .bg__orb--a {
  animation: drift-a var(--drift-a) var(--drift-ease) infinite;
}
.bg[data-bg-mode="gradient_animated"]:not([data-bg-speed="off"]) .bg__orb--b {
  animation: drift-b var(--drift-b) var(--drift-ease) infinite;
}
.bg[data-bg-mode="gradient_animated"]:not([data-bg-speed="off"]) .bg__orb--c {
  animation: drift-c var(--drift-c) var(--drift-ease) infinite;
}

/* Three distinct paths — not one path mirrored, which the eye reads as a pair. */
@keyframes drift-a {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  33%  { transform: translate3d(12vw, 8vh, 0) scale(1.08); }
  66%  { transform: translate3d(-6vw, 14vh, 0) scale(0.96); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}
@keyframes drift-b {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  33%  { transform: translate3d(-14vw, -6vh, 0) scale(0.94); }
  66%  { transform: translate3d(-4vw, 10vh, 0) scale(1.10); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}
@keyframes drift-c {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  33%  { transform: translate3d(7vw, -12vh, 0) scale(1.05); }
  66%  { transform: translate3d(16vw, 4vh, 0) scale(0.92); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}

/* Hidden tab: stop burning frames on something nobody is looking at.
   !important on purpose — the `animation` shorthand above resets play-state to
   running, at a specificity this selector can at best tie. A tie decided by
   source order is a silent failure waiting for someone to move a rule, and a
   background that never actually pauses looks exactly like one that does. */
.bg--paused .bg__orb { animation-play-state: paused !important; }

/* Reduced motion freezes the movement; it does not take the colour away.
   Someone who asked for less motion did not ask for a greyer app. */
@media (prefers-reduced-motion: reduce) {
  .bg__orb { animation: none !important; }
}

/* --- Scrim: the contrast guarantee ---------------------------------------
   Sits over the orbs and under the content. --bg-veil is driven by the
   intensity control and raised further by appearance.js when the chosen palette
   is too close in luminance to the text. */
.bg__scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim, transparent);
  transition: background 240ms var(--ease);
}
[data-theme="light"] .bg { --scrim: rgba(255, 255, 255, var(--bg-veil)); }
[data-theme="dark"]  .bg { --scrim: rgba(10, 10, 12, var(--bg-veil)); }
/* "Do tema" ships a background already tuned against the text; veiling it would
   only wash out a palette that is known to be safe. */
.bg[data-bg-mode="system"] { --scrim: transparent; }

/* ===========================================================================
   Vidro extras
   =========================================================================== */

/* The panels' own backdrop-filter already blurs whatever shows through, so a
   90px orb blur underneath is work that produces no visible difference. */
[data-theme-style="vidro"] .bg { --orb-blur: 70px; }

/* ===========================================================================
   Rim reflection — Vidro only
   ---------------------------------------------------------------------------
   There is ONE virtual light, at the pointer, in viewport coordinates. Each
   glass surface works out the angle that light reaches it from, given where it
   sits on screen: a surface to the left of the cursor lights its right corner,
   one above it lights its bottom corner. That shared origin is what the eye
   reads as "same room, same lamp" — per-surface highlights that always sit
   under the cursor read as a set of blinking frames instead.

   The reflection lives ONLY on the edge. Nothing is ever painted across the
   face of a surface; no light crosses the content.

   Three variables per surface and no more:
     --mx/--my  where the light falls, in this surface's own coordinates (as a
                percentage, free to sit outside 0-100% when the light is off to
                one side)
     --glow     normalised proximity, 0..1, modulating intensity

   The selector list is every surface that paints var(--panel), i.e. everything
   that actually turns to glass in this style. Month cells are deliberately out:
   there are forty of them in a grid and they are cells, not cards.
   KEEP IN SYNC with GLASS in static/js/rim.js.
   =========================================================================== */

[data-theme-style="vidro"] :is(.panel, .metric-card, .summary-card,
                               .task-col, .ins-card, .funnel-col, .pipe-col) {
  position: relative;
  transition: transform 240ms var(--ease);
  /* --shadow-card carries the drop shadow and the inset band that makes the face
     fall off from top to bottom; the two inset highlights after it are the
     bevel: a bright lip along the top edge and a soft ring just inside the
     border, which is what gives the material thickness. Static on purpose —
     see the note on the thread below. */
  box-shadow: var(--shadow-card),
              inset 0 1.5px 0 var(--glass-lip),
              inset 0 0 14px -5px var(--glass-ring);
}

/* The specular thread — the ONLY thing that follows the pointer. 1.5px on the
   exact corner, anchored at the light.

   The bevel that used to live here as a second tracking layer is now static
   inset light on the surface above. Measured: the two layers together ran at
   33.4ms a frame, the thread alone at 17.0ms — the wide band was costing 16.6ms
   every frame to repaint something that never needed to move. Thickness is a
   property of the material and holds still; only the specular return travels. */
[data-theme-style="vidro"] :is(.panel, .metric-card, .summary-card,
                               .task-col, .ins-card, .funnel-col, .pipe-col)::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.5px;
  background: radial-gradient(210px circle at var(--mx, 50%) var(--my, 0%),
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 255, 255, .30) 42%,
    rgba(255, 255, 255, .06) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
  /* A surface far from the light barely returns any: the floor is low so the
     rim fades out with distance instead of ringing every card on screen at half
     brightness. That is both closer to how light behaves and what lets rim.js
     stop repainting the ones that are dark anyway. */
  opacity: calc(.14 + var(--glow, 0) * .86);
  pointer-events: none;
}

/* Compression on press: 120ms in, 240ms back out (the base transition above).
   Fast to the press and slow on the return is how mass behaves; matched timings
   read as a toy elastic. The :has() guard keeps a click on a field or a button
   from shrinking the whole surface wrapped around it — and where :has() is
   unsupported the whole rule drops, which costs a flourish, not a feature. */
[data-theme-style="vidro"] :is(.panel, .metric-card, .summary-card,
                               .task-col, .ins-card, .funnel-col, .pipe-col):active:not(:has(input:active, textarea:active, select:active, button:active, a:active)) {
  transform: scale(.985);
  transition-duration: 120ms;
}

/* --- Off conditions -------------------------------------------------------
   Each of these also stops the animation loop in rim.js. Hiding the effect
   while the loop keeps running is the worst of both worlds. */
[data-reduce-transparency="1"] :is(.panel, .metric-card, .summary-card,
                                   .task-col, .ins-card, .funnel-col, .pipe-col)::after,
[data-rim="off"] :is(.panel, .metric-card, .summary-card,
                     .task-col, .ins-card, .funnel-col, .pipe-col)::after { display: none; }

/* Reduced motion and touch keep the material and drop the tracking: the light
   parks near the top-left corner, the convention for interface lighting, at
   half intensity. Someone who asked for less motion did not ask for flat panels,
   and a fingertip is not a cursor to follow. */
@media (prefers-reduced-motion: reduce) {
  [data-theme-style="vidro"] :is(.panel, .metric-card, .summary-card,
                                 .task-col, .ins-card, .funnel-col, .pipe-col) {
    --mx: 12%;
    --my: 0%;
    --glow: .5;
  }
  [data-theme-style="vidro"] :is(.panel, .metric-card, .summary-card,
                                 .task-col, .ins-card, .funnel-col, .pipe-col):active {
    transform: none;
  }
}
@media (hover: none), (pointer: coarse) {
  [data-theme-style="vidro"] :is(.panel, .metric-card, .summary-card,
                                 .task-col, .ins-card, .funnel-col, .pipe-col) {
    --mx: 12%;
    --my: 0%;
    --glow: .5;
  }
}

/* ===========================================================================
   Appearance controls (Ajustes)
   =========================================================================== */
.appearance-sep {
  border: 0;
  border-top: 1px solid var(--panel-border);
  margin: var(--space-5) 0;
}
.appearance-note {
  font-size: 12px;
  color: var(--text-3);
  margin-top: var(--space-2);
}
.appearance-group__title {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-2);
  margin-bottom: var(--space-2);
}

/* --- Accent picker -------------------------------------------------------- */
.swatches { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }
.swatch {
  width: 28px; height: 28px; flex: 0 0 28px;
  border-radius: 50%; border: none; cursor: pointer; padding: 0;
  background: hsl(var(--sw-h) 91% 52%);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
  transition: transform .12s var(--ease), box-shadow .12s var(--ease);
}
[data-theme="dark"] .swatch { background: hsl(var(--sw-h) 91% 62%); }
.swatch:hover { transform: scale(1.08); }
.swatch.is-active {
  box-shadow: 0 0 0 2px var(--panel-solid), 0 0 0 4px var(--text-2);
}

.hue-row { display: flex; align-items: center; gap: var(--space-3); }
.hue-preview {
  width: 30px; height: 30px; flex: 0 0 30px; border-radius: 50%;
  background: var(--ui-accent);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
}

/* --- Range inputs (no native styling is usable across the three engines) --- */
.range {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 22px; background: transparent; cursor: pointer; margin: 0;
}
.range::-webkit-slider-runnable-track { height: 6px; border-radius: 3px; background: var(--field); }
.range::-moz-range-track { height: 6px; border-radius: 3px; background: var(--field); }
.range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 18px; height: 18px; margin-top: -6px;
  border-radius: 50%; background: #fff;
  border: 1px solid rgba(0, 0, 0, .18);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .28);
}
.range::-moz-range-thumb {
  width: 18px; height: 18px;
  border-radius: 50%; background: #fff;
  border: 1px solid rgba(0, 0, 0, .18);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .28);
}
.range--hue::-webkit-slider-runnable-track,
.range--hue::-moz-range-track {
  background: linear-gradient(to right,
    hsl(0 91% 55%), hsl(60 91% 55%), hsl(120 91% 55%), hsl(180 91% 55%),
    hsl(240 91% 55%), hsl(300 91% 55%), hsl(360 91% 55%));
}
.range-row { display: flex; align-items: center; gap: var(--space-3); }
.range-row .range { flex: 1; }
.range-value {
  flex: 0 0 46px; text-align: right; font-size: 13px; color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

/* --- Background presets --------------------------------------------------- */
.preset-grid { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.preset {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  border: none; background: none; padding: 0; cursor: pointer;
  color: var(--text-2); font-size: 11px;
}
.preset__thumb {
  width: 44px; height: 44px; border-radius: 50%;
  background: linear-gradient(135deg, var(--p-stops));
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
  transition: transform .12s var(--ease), box-shadow .12s var(--ease);
}
.preset:hover .preset__thumb { transform: scale(1.06); }
.preset.is-active .preset__thumb { box-shadow: 0 0 0 2px var(--panel-solid), 0 0 0 4px var(--ui-accent); }
.preset.is-active { color: var(--text); font-weight: 600; }
.preset-grid.is-locked { opacity: .45; pointer-events: none; }

/* --- Background colour list ----------------------------------------------- */
.bgcolors { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.bgcolor-wrap { position: relative; display: inline-flex; }
.bgcolor {
  width: 34px; height: 34px; flex: 0 0 34px;
  border-radius: 50%; border: none; padding: 0; cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
  transition: box-shadow .12s var(--ease);
}
.bgcolor.is-editing { box-shadow: 0 0 0 2px var(--panel-solid), 0 0 0 4px var(--ui-accent); }
.bgcolor__del {
  position: absolute; top: -4px; right: -4px;
  width: 16px; height: 16px; border-radius: 50%;
  border: none; padding: 0; cursor: pointer;
  background: var(--text-2); color: var(--panel-solid);
  font-size: 11px; line-height: 16px;
}
.bgcolor-add {
  width: 34px; height: 34px; flex: 0 0 34px;
  border-radius: 50%; cursor: pointer; padding: 0;
  border: 1px dashed var(--text-3); background: none; color: var(--text-2);
  font-size: 17px; line-height: 1;
}
/* The HSL editor for the selected colour. Three sliders instead of a native
   colour input on purpose: <input type="color"> speaks hex only, and hex round
   trips would break the "colours are HSL end to end" rule. */
.hsl-editor { margin-top: var(--space-3); display: grid; gap: var(--space-2); }
.hsl-editor__label { font-size: 11px; color: var(--text-2); }
