/* DBOT QS — the premium layer.
   ===========================================================================
   "Smooth motion, visually appealing, a premium look and feel -- you can use
   the glassmorphic style." Loaded after `theme.css` and before `mobile.css`,
   and additive like both: nothing in the app reads a class defined here, no
   screen script names it, and taking out its two lines in `index.html` (this
   sheet and `premium.js`) puts back the flat finish exactly as it was.

   `theme.css` says "no gradients, no glass" and that was the right call for
   the look it made. This one changes the direction on request, and keeps the
   reasons the old rule existed:

   * **Frost only where something moves behind it.** The panel, the palette
     and the toast open *over* a screen, and the blur is what keeps the
     screen legible-but-behind. The bar, the rail, the tiles and the cards
     sit over nothing but the still ground, and blurring a smooth gradient
     draws the same pixels as not blurring it -- measured: 0 difference on a
     tile, under 1.6/255 on the rail -- while each blurred pane is a GPU
     pass on every frame anything near it moves. Removing those four cut the
     compositor's draw time by 60% on a tile hover and 90% on a navigation.
     They are tinted glass, rim and all, and unblurred.
   * **Grids do not move.** An editable cell is still opaque white and a
     computed one still grey ("white is yours, grey is the system's"); typing,
     pasting and filling animate nothing, because an action repeated a
     hundred times an hour must not make anybody wait for it.
   * **One motion vocabulary.** The tokens below replace `theme.css`'s, so
     every existing transition in the app picks up the new curves without a
     rule of its being touched: an exponential ease-out for anything
     arriving, the drawer curve for anything that slides in from an edge,
     and no overshoot anywhere -- a figure that bounces reads as uncertain.
   * **Transparency is a preference too.** `prefers-reduced-transparency`,
     `prefers-contrast: more`, `forced-colors` and a browser without
     `backdrop-filter` all get solid surfaces; `prefers-reduced-motion` gets
     no movement. Every text on glass keeps 4.5:1 -- measured, not assumed.
   =========================================================================== */

:root {
  /* ---- motion ------------------------------------------------------------
     An ease-out that is fast at the start and long in the tail, so a thing
     responds the instant it is touched and settles rather than stops. The
     drawer curve is the one iOS sheets use: quick to leave the edge, slow to
     arrive. Exits are shorter than entrances and never ease *in* from rest
     -- a menu that hesitates before leaving feels like it did not hear. */
  --ease:        cubic-bezier(.23, 1, .32, 1);
  /* The token `theme.css` gives every exit. An ease-*in* delays the one
     moment the eye is on -- the instant something starts to leave -- so
     exits take the same strong ease-out, and are simply shorter. */
  --ease-in:     cubic-bezier(.23, 1, .32, 1);
  --ease-in-out: cubic-bezier(.77, 0, .175, 1);
  --ease-drawer: cubic-bezier(.32, .72, 0, 1);
  --ease-spring: cubic-bezier(.32, .72, 0, 1);
  --t-fast:  120ms;
  --t-base:  180ms;
  --t-enter: 300ms;
  --t-exit:  160ms;
  --tempo: var(--t-base);

  /* ---- the ground --------------------------------------------------------
     DBOT's blue, lit from two corners and a little of the mark's teal, over
     a cool grey. Low enough that nothing on it has to fight it: the
     brightest glow moves the ground by 6% lightness, not a colour change. */
  --canvas: #eef2f8;
  --glow-a: rgba(48, 80, 200, .16);
  --glow-b: rgba(0, 142, 190, .10);
  --glow-c: rgba(0, 32, 96, .07);

  /* ---- glass -------------------------------------------------------------
     Three strengths, by what sits beneath: the rail over the still ground,
     the bar and the overlays over moving content, and the cards the figures
     are read on, which are nearly solid. */
  --glass:        rgba(255, 255, 255, .62);
  --glass-strong: rgba(255, 255, 255, .80);
  --glass-card:   rgba(255, 255, 255, .88);
  --glass-line:   rgba(255, 255, 255, .72);
  --glass-edge:   rgba(15, 23, 42, .09);
  --glass-hi:     inset 0 1px 0 rgba(255, 255, 255, .75);
  --glass-blur:   blur(20px) saturate(1.7);
  --glass-blur-sm: blur(12px) saturate(1.5);
  --veil:         rgba(15, 23, 42, .22);

  /* The shadows, a touch deeper and bluer: on a tinted ground a neutral
     grey shadow reads as dirt, a shadow in the ground's own hue as depth. */
  --e1: 0 1px 2px rgba(20, 36, 80, .05);
  --e2: 0 1px 2px rgba(20, 36, 80, .05), 0 4px 14px -4px rgba(20, 36, 80, .10);
  --e3: 0 2px 4px rgba(20, 36, 80, .05), 0 12px 28px -8px rgba(20, 36, 80, .18);
  --e4: 0 4px 8px rgba(20, 36, 80, .06), 0 24px 56px -12px rgba(20, 36, 80, .30);
  --shadow: var(--e2);
}

:root[data-theme="dark"] {
  --canvas: #0c111b;
  --glow-a: rgba(64, 104, 255, .20);
  --glow-b: rgba(0, 150, 210, .11);
  --glow-c: rgba(0, 40, 120, .30);

  --glass:        rgba(20, 26, 38, .60);
  --glass-strong: rgba(22, 28, 42, .82);
  --glass-card:   rgba(26, 31, 42, .88);
  --glass-line:   rgba(255, 255, 255, .07);
  --glass-edge:   rgba(0, 0, 0, .45);
  --glass-hi:     inset 0 1px 0 rgba(255, 255, 255, .05);
  --veil:         rgba(0, 0, 0, .45);

  --e1: 0 1px 2px rgba(0, 0, 0, .40);
  --e2: 0 1px 2px rgba(0, 0, 0, .40), 0 6px 16px -6px rgba(0, 0, 0, .50);
  --e3: 0 2px 4px rgba(0, 0, 0, .40), 0 14px 30px -10px rgba(0, 0, 0, .60);
  --e4: 0 4px 8px rgba(0, 0, 0, .40), 0 28px 60px -14px rgba(0, 0, 0, .70);
  --shadow: var(--e2);
}

/* ===========================================================================
   The ground
   =========================================================================== */

/* Painted once, on a layer of its own, never animated, never blurred: the
   glass above it gets something to be glass over.

   Its own layer, and not `background-attachment: fixed` on the body. A
   fixed background is part of the page's base layer, and every screen
   draws into that layer -- `#main` is transparent so the ground shows
   through -- so each hover highlight, typed cell or navigation re-rastered
   three viewport-sized gradients under it. On a fixed, promoted element it
   is rastered once and composited under everything after that.

   Off a project -- the sign-in page has its own ground -- it stays put too;
   the sign-in's `#app.signed-out` rules paint over it. */
body { background-color: var(--canvas); }
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    radial-gradient(1100px 620px at 12% -8%, var(--glow-a), transparent 62%),
    radial-gradient(900px 560px at 108% 12%, var(--glow-b), transparent 60%),
    radial-gradient(1200px 700px at 70% 118%, var(--glow-c), transparent 64%);
  will-change: transform;
}
#main { background: transparent; }

/* ===========================================================================
   Glass: the chrome that floats
   =========================================================================== */

/* The bar and the rail are glass without the frost: nothing scrolls under
   either -- they are the grid's own rows and columns, over the still ground
   -- so a blur here would draw the same pixels at the cost of a GPU pass per
   frame. It also made the bar a backdrop root, which is what left the
   profile menu inside it unable to frost anything. `theme.css`'s own
   `saturate(1.1)` goes too, for the same two reasons. */
#topbar {
  background: var(--glass-strong);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-bottom: 1px solid var(--glass-edge);
  box-shadow: var(--glass-hi);
  transition: box-shadow var(--t-base) var(--ease),
              background-color var(--t-base) var(--ease);
}
#topbar.lifted {
  box-shadow: var(--glass-hi), 0 1px 0 var(--glass-edge),
              0 10px 24px -14px rgba(20, 36, 80, .35);
}

#sidebar {
  background: var(--glass);
  border-right: 1px solid var(--glass-edge);
  box-shadow: inset -1px 0 0 var(--glass-line);
}
/* On a phone the rail is a drawer over the screen, and the screen under it
   is not a still ground: a navy card header slid behind "Cost Analysis" as
   a blue smear. Solid there. */
@media (max-width: 900px) {
  #sidebar { background: var(--surface); }
}
/* The panel slides in from the edge it lives on, on the drawer curve, and
   is frosted: the screen it is explaining stays visible behind it. */
#panel {
  background: var(--glass-strong);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-left: 1px solid var(--glass-edge);
  box-shadow: var(--e4), inset 1px 0 0 var(--glass-line);
  transition: opacity var(--t-exit) var(--ease-in),
              transform var(--t-exit) var(--ease-in),
              display var(--t-exit) allow-discrete;
}
/* A working is opened tens of times a day -- every grey figure is one
   click from it -- so the panel is quick: 280ms on the drawer curve, 24px of
   travel, and out in 160ms. */
#panel:not([hidden]) {
  transition: opacity 200ms var(--ease),
              transform 280ms var(--ease-drawer),
              display 280ms allow-discrete;
}
#panel[hidden] { transform: translateX(16px); }
@starting-style {
  #panel:not([hidden]) { opacity: 0; transform: translateX(24px); }
}
.panel-head {
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  -webkit-backdrop-filter: var(--glass-blur-sm);
  backdrop-filter: var(--glass-blur-sm);
  border-bottom: 1px solid var(--glass-edge);
}

/* The toast: a dark lozenge of glass, rising from the edge it is anchored
   to. Ink stays ink -- #fff on the tinted dark is well over 7:1. */
#toast {
  background: color-mix(in srgb, var(--tip-bg) 88%, transparent);
  color: var(--tip-ink);
  -webkit-backdrop-filter: var(--glass-blur-sm);
  backdrop-filter: var(--glass-blur-sm);
  border: 1px solid rgba(255, 255, 255, .10);
  border-radius: 12px;
  box-shadow: var(--e4), inset 0 1px 0 rgba(255, 255, 255, .08);
}
#toast.bad { background: color-mix(in srgb, var(--bad) 92%, #000); color: #fff; }
#toast:not([hidden]) {
  transition: opacity 200ms var(--ease), transform 260ms var(--ease-drawer),
              display 260ms allow-discrete;
}
/* It is centred by `translateX(-50%)`, and the finish layer's entrance and
   exit wrote transforms that replaced it -- so the toast slid in diagonally
   from half its own width to the right, and left the same way. It rises
   straight up from the edge it is anchored to and sinks straight back. */
#toast { transform-origin: 50% 100%; }
#toast[hidden] { transform: translate(-50%, 8px) scale(.98); }
@starting-style {
  #toast:not([hidden]) { opacity: 0; transform: translate(-50%, 12px) scale(.97); }
}

/* The palette and its veil: the page dims and softens behind the one thing
   being asked. And it is *there*, not arriving: Ctrl+K is pressed a hundred
   times a day, and a keyboard shortcut that animates feels like it did not
   hear you (Raycast has no open animation, for that reason). The finish
   layer's 120ms entrance is switched off here with the rest of the motion
   this layer decided against. */
.palette-veil {
  background: var(--veil);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  animation: none;
}
.palette {
  background: var(--glass-strong);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-line);
  border-radius: 16px;
  box-shadow: var(--e4), var(--glass-hi);
  animation: none;
}
.palette input {
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  border-radius: 10px;
}

/* The profile menu: solid, with the glass's edge and lift. It was frosted
   like the palette, but it opens from *inside* the top bar, and the bar's
   backdrop-filter made the bar the menu's backdrop root: the menu's blur
   could only ever see the inside of the bar. What lay under it on the page
   -- "Spend to date", a navy table header -- came through unblurred at 20%,
   crisp enough to read through "My Projects" ("this is so transparent").
   The bar is no longer blurred, but a menu is a list to be read, so it
   stays solid; the rim, the hairline and the shadow keep it the same
   object. */
.profile-menu {
  /* Fully opaque: at 98% a dashboard figure in 40px type still showed
     through as a grey "913" beside "Role". */
  background: var(--surface);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border: 1px solid var(--glass-edge);
  box-shadow: var(--e4), var(--glass-hi);
}

/* ===========================================================================
   Surfaces the figures are read on
   =========================================================================== */

/* Nearly solid and not blurred -- see the note at the top. The white rim on
   top is the whole of the "glass": light catching an edge, which is what
   makes a panel read as a pane rather than as a box with a border. */
.card {
  background: var(--glass-card);
  border-color: var(--glass-line);
  box-shadow: var(--e2), var(--glass-hi);
  border-radius: 12px;
}
/* And it does not lift under the pointer: a card is where a grid is read,
   and a surface that reacts every time the hand crosses it is a surface
   that will not hold still while somebody reads a figure off it. */

/* The tiles: the figures a reader comes to the screen for -- the stronger
   glass, with a rim of light, and no frost: over the still ground it drew
   nothing (0/255 measured) and a tile that lifts or tilts under the pointer
   had to re-blur on every frame of it. A tile or card you can open carries a lamp (`premium.js`): a soft
   glow in the accent that trails the pointer across it. The dashboard's own
   tiles have a spotlight of their own and do not get a second. */
.tile, .proj-card {
  position: relative; isolation: isolate;
  background: var(--glass-strong);
  border-color: var(--glass-line);
  box-shadow: var(--e1), var(--glass-hi);
  border-radius: 12px;
  transition: box-shadow var(--t-base) var(--ease),
              border-color var(--t-base) var(--ease),
              transform var(--t-fast) var(--ease);
}
.pr-lamp {
  position: absolute; inset: 0; z-index: -1; overflow: hidden;
  border-radius: inherit; pointer-events: none;
  opacity: 0; transition: opacity var(--t-base) var(--ease);
}
.pr-lamp > i {
  position: absolute; left: 0; top: 0; width: 260px; height: 260px;
  border-radius: 50%;
  background: radial-gradient(closest-side,
              color-mix(in srgb, var(--accent) 14%, transparent), transparent);
  /* The trail: a transition, so the lamp retargets from wherever it is each
     time the pointer moves rather than restarting -- weight without a
     frame loop. */
  transition: transform 280ms var(--ease);
  will-change: transform;
}

/* Hover moves things only where there is a pointer to hover with. On a
   touch screen a tap fires :hover and leaves it stuck, and a card that lifts
   under the thumb pressing it reads as a fault. A tens-of-times-a-day hover
   gets the smallest lift that still says "this responds": one pixel. */
@media (hover: hover) and (pointer: fine) {
  .tile.clickable:hover, .proj-card:hover {
    transform: translateY(-1px);
    box-shadow: var(--e3), var(--glass-hi);
    border-color: color-mix(in srgb, var(--accent) 22%, var(--glass-line));
  }
  .tile.clickable:hover > .pr-lamp, .proj-card:hover > .pr-lamp { opacity: 1; }
}
@media (hover: none), (pointer: coarse) {
  .tile.clickable:hover, .proj-card:hover { transform: none; }
}
/* Pressed is quicker than hovered, and goes back in the same 120ms. */
.tile.clickable:active, .proj-card:active {
  transform: scale(.99);
  box-shadow: var(--e1), var(--glass-hi);
}
.tile-row.spotlit .tile.clickable:hover { transform: none; }

/* The dashboard's spotlight keeps its tilt and glow, with the two hover
   timings that ran past the UI budget brought inside it: the sweep crossed
   the card in 700ms and the underline grew in 500ms, on something crossed
   tens of times a day. */
.tile-sweep { transition-duration: 300ms; }
.tile-line  { transition-duration: 260ms; }

/* ===========================================================================
   Controls
   =========================================================================== */

/* Pressed is a squeeze, not a drop: 97%, the size a finger-press makes a
   real button look, back in 120ms. Scale leaves the label where it was, so
   the eye does not track a moving word. */
button.btn {
  transition: background-color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              box-shadow var(--t-base) var(--ease),
              transform var(--t-fast) var(--ease);
}
button.btn:active:not(:disabled) { transform: scale(.97); }
/* The primary button carries light on its top edge -- the one gradient in
   the layer that is a surface and not a decoration. */
button.btn.primary {
  background-image: linear-gradient(rgba(255, 255, 255, .10), rgba(255, 255, 255, 0));
  box-shadow: var(--e2), inset 0 1px 0 rgba(255, 255, 255, .14);
}
button.btn.primary:hover:not(:disabled) {
  box-shadow: var(--e3), inset 0 1px 0 rgba(255, 255, 255, .18);
}

/* A field gets a soft halo in the accent as it takes focus, and gives it
   back as smoothly. The ring is still the accent, still 3px: this changes
   how it arrives, not what it is. */
select.sel-input, input.text-input, .field textarea.text-input {
  background-color: color-mix(in srgb, var(--surface) 88%, transparent);
  transition: border-color var(--t-fast) var(--ease),
              box-shadow var(--t-base) var(--ease),
              background-color var(--t-fast) var(--ease);
}
select.sel-input:focus, input.text-input:focus, .field textarea.text-input:focus {
  background-color: var(--surface);
  box-shadow: 0 0 0 3px var(--accent-soft),
              0 0 0 6px color-mix(in srgb, var(--accent) 8%, transparent);
}

/* The chips that do something press like buttons. */
.chip.act:active { transform: scale(.97); }

/* ===========================================================================
   Arrival
   =========================================================================== */

/* The heading of a screen settles in a beat before its content, so the eye
   lands on the title first -- a 4px rise and a fade, 300ms. Content under
   it keeps `theme.css`'s own entrance and `motion.js`'s card stagger. */
#main > .screen > .screen-head { animation: head-in 200ms var(--ease) backwards; }
#main > .screen.redraw > .screen-head { animation: none; }
@keyframes head-in {
  from { opacity: 0; transform: translateY(4px); }
}

/* A screen's tiles come in as a set, 40ms apart, once per screen.
   Opacity and scale only: `motion.js` already brings the row up as a card,
   and a second rise inside it would double the distance travelled. */
#main > .screen > .tile-row > .tile { animation: tile-in 240ms var(--ease) backwards; }
#main > .screen > .tile-row > .tile:nth-child(2) { animation-delay: 40ms; }
#main > .screen > .tile-row > .tile:nth-child(3) { animation-delay: 80ms; }
#main > .screen > .tile-row > .tile:nth-child(4) { animation-delay: 120ms; }
#main > .screen > .tile-row > .tile:nth-child(n+5) { animation-delay: 160ms; }
#main > .screen.redraw > .tile-row > .tile { animation: none; }
@keyframes tile-in {
  from { opacity: 0; transform: scale(.97); }
}

/* ===========================================================================
   When the glass should not be glass
   =========================================================================== */

/* No `backdrop-filter`: a frosted pane without the frost is a see-through
   one, and text over a see-through pane is text over whatever scrolls
   beneath it. Solid, then -- for the panes that are frosted because
   something *does* move beneath them. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  #panel, .palette, .panel-head { background: var(--surface); }
  #toast { background: var(--tip-bg); }
}

/* The person has asked for less transparency, or more contrast: every pane
   goes solid and every rim goes to the plain line. */
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
  #topbar, #sidebar, #panel, .palette, .profile-menu, .tile, .proj-card,
  .panel-head, .card {
    background: var(--surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border-color: var(--line);
  }
  #toast { background: var(--tip-bg); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .palette-veil { -webkit-backdrop-filter: none; backdrop-filter: none; }
  body::before { display: none; }
}

@media (forced-colors: active) {
  body::before { display: none; }
  .pr-lamp { display: none; }
}

/* Reduced motion is gentler, not frozen: nothing moves, but what arrives
   still fades in -- the fade is what tells the eye something changed. The
   lamp is not drawn and the press does not squeeze. */
@media (prefers-reduced-motion: reduce) {
  #main > .screen > .screen-head,
  #main > .screen > .tile-row > .tile { animation: fade-in 160ms linear backwards; }
  .tile.clickable:hover, .proj-card:hover,
  .tile.clickable:active, .proj-card:active,
  button.btn:active:not(:disabled), .chip.act:active { transform: none; }
  .pr-lamp { display: none; }
  #panel[hidden] { transform: none; }
  #toast[hidden] { transform: translateX(-50%); }
  #panel:not([hidden]), #toast:not([hidden]) {
    transition: opacity 160ms linear, display 160ms allow-discrete;
  }
}
@media (prefers-reduced-motion: reduce) {
  @starting-style {
    #panel:not([hidden]) { transform: none; }
    #toast:not([hidden]) { transform: translateX(-50%); }
  }
}
@keyframes fade-in {
  from { opacity: 0; }
}
