*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{font-family:var(--font-ui);color:var(--ink);overflow:hidden;
  /* every figure in the app lines up: Syne's tnum makes the ten digits one width, so a running
     clock, a tempo or a temperature never shuffles the text beside it */
  font-variant-numeric:tabular-nums;font-feature-settings:'tnum' 1}

/* ============ background ============
   A flat themed wash, not a picture: the weather is stated by the icon beside the temperature. */
/* The tint lives at the top and is gone by the middle of the page, the way the sky used to
   fade out behind the panels. Below that the surface is neutral, so the lower blocks keep
   their own colour instead of taking a cast from the background. */
body{background:
    linear-gradient(180deg,
      rgba(140,190,238,.92) 0%,
      rgba(168,206,242,.62) 16%,
      rgba(200,225,246,.30) 32%,
      rgba(226,238,250,.10) 46%,
      rgba(255,255,255,0) 58%),
    #eef2f7;
  background-attachment:fixed}
[data-theme="dark"] body{background:
    linear-gradient(180deg,
      rgba(126,146,176,.30) 0%,
      rgba(108,128,158,.18) 18%,
      rgba(86,104,132,.09) 34%,
      rgba(60,74,96,.03) 48%,
      rgba(0,0,0,0) 60%),
    #10151c;
  background-attachment:fixed}
.skycanvas,.scrim{display:none}

/* ============ app shell (full viewport) ============ */
.app{position:relative;z-index:1;height:100vh;display:flex;flex-direction:column;padding:14px 20px;
  width:100%;max-width:var(--shell-max);margin-inline:auto}
.glass{backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  background:var(--glass-bg);border:var(--glass-brd);box-shadow:var(--glass-edge),var(--glass-shadow)}

/* ---- top bar (nav only) ---- */
/* the bar has to sit above the panels below it, or the notification list is drawn over */
.topbar{position:relative;z-index:50;flex:none;display:flex;align-items:center;justify-content:space-between;gap:16px;padding:13px 20px;border-radius:var(--r-2)}
.tb-l{display:flex;align-items:center;gap:24px}
/* Live text, not the outlined SVG: at 25px the font renderer hints the stems and the edges come
   out cleaner than any path can. The outlines stay in assets/brand for icons and for print. */
.word{font-family:var(--font-ui);font-size:27.5px;letter-spacing:.02em;font-weight:700;
  color:var(--ink);text-decoration:none;cursor:pointer;-webkit-font-smoothing:antialiased}
.word:hover{opacity:.75}
.word:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:4px}
.menu{display:flex;gap:4px}
.menu a{font-size:13.5px;font-weight:500;color:var(--muted);text-decoration:none;padding:9px 16px;border-radius:var(--r-1);cursor:pointer;transition:var(--t-fast)}
.menu a:hover{color:var(--ink);background:var(--surface-hover)}
.menu a.on{background:rgba(62,133,211,.16);color:var(--accent-deep);font-weight:500}
.tb-r{display:flex;align-items:center;gap:16px}
.live{font-size:11px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;
  display:flex;align-items:center;gap:7px;color:#c0392f;transition:color 200ms ease-out}
.live::before{content:'';width:8px;height:8px;border-radius:50%;background:currentColor;
  box-shadow:0 0 10px currentColor;transition:background 200ms ease-out}
.live.on{color:#1d9a5f}
.live.on::before{animation:pulse 2.4s ease-in-out infinite}
[data-theme="dark"] .live{color:#e8736a}
[data-theme="dark"] .live.on{color:#4fd08a}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
/* square, like every cover in the app - and it holds the venue's logo once there is one */
.ava{width:42px;height:42px;padding:0;border:none;border-radius:7px;
  background-size:cover;background-position:center;
  display:grid;place-items:center;color:#fff;font-size:17px;font-weight:700;letter-spacing:.02em;
  cursor:pointer;flex:none;transition:var(--t-fast)}
.ava:hover{box-shadow:0 0 0 3px rgba(62,133,211,.18)}
.ava.has-logo{border:1px solid rgba(20,40,70,.16)}
.venue-id{display:flex;flex-direction:column;align-items:flex-end;text-align:right;min-width:0}

/* ---- hero: artwork on the left, day and weather stacked on the right ---- */
.hero{position:relative;flex:none;display:flex;align-items:flex-end;justify-content:space-between;gap:24px;padding:17.5px 8px 24.5px;color:var(--ink)}   /* the artwork sits centred in the band:
   equal air above it to the top bar and below it to the panels */
/* display:flex outranks the hidden attribute, so without this the band claims to be gone and stays */
.hero[hidden]{display:none}
.hero-day,.hero-date,.hero-temp,.hero-cond,.hero-venue,.np2-eyebrow,.np2-title,.np2-artist{text-shadow:none}
.hero-day{font-family:var(--font-display);font-size:28px;font-weight:600;line-height:1;letter-spacing:.01em}
.hero-date{font-size:11px;letter-spacing:.2em;text-transform:uppercase;opacity:.85;margin-top:5px;white-space:nowrap}
.hero-r{display:flex;flex-direction:column;align-items:flex-end;text-align:right;align-self:flex-start;padding-right:16px}
/* the day on top, the weather under it, both hard to the right edge */
/* Icon and reading both sit ON the bottom edge of the artwork opposite - align to the baseline
   of the block, not its centre, or the words float half a line above the line they should meet. */
.hero-wx{display:flex;align-items:flex-end;justify-content:flex-end;gap:14px;margin-top:2px}
/* the condition reads under the temperature, not beside it */
.hero-wxtx{display:flex;flex-direction:column;align-items:center;justify-content:flex-end}
/* one ink, not the set's own palette: black on light, white on dark */
.hero-wx .wx{width:95px;height:95px;flex:none;display:block;
  filter:brightness(0) saturate(100%)}
[data-theme="dark"] .hero-wx .wx{filter:brightness(0) saturate(100%) invert(1)}
.hero-when{text-align:right}
.hero-temp{font-family:var(--font-display);font-size:44.9px;font-weight:600;line-height:1}
/* the block is anchored on its bottom, so widening this gap lifts the temperature and leaves
   the condition where it is */
.hero-cond{font-size:11px;letter-spacing:.16em;text-transform:uppercase;opacity:.9;
  margin-top:9px;text-align:center}
.hero-venue{font-size:11.5px;opacity:.8;margin-top:10px}

.np2{display:flex;align-items:flex-end;gap:18px}
/* the artwork IS the play control: press it to start or stop the room */
.np2-cover{position:relative;width:134px;height:134px;padding:0;border:none;cursor:pointer;flex:none;
  border-radius:7px;background-size:cover;background-position:center;overflow:hidden;
  box-shadow:0 16px 38px rgba(0,0,0,.42),inset 0 0 0 1px rgba(255,255,255,.18);
  transition:transform 150ms ease-out,box-shadow 200ms ease-out}
.np2-cover:hover{box-shadow:0 18px 44px rgba(0,0,0,.48),inset 0 0 0 1px rgba(255,255,255,.3)}
.np2-cover:active{transform:scale(.96)}
.np2-cover:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
/* the beat, felt on the outline: rings leave the artwork on every kick, the edge itself
   brightens with the low end, and the whole square breathes a fraction of a percent. */
.np2-art{position:relative;flex:none;line-height:0;transform-origin:50% 50%;will-change:transform}
.np2-edge,.np2-ring{position:absolute;inset:0;border-radius:7px;pointer-events:none}
.np2-edge{border:1px solid rgba(255,255,255,.16);z-index:3}
.np2-ring{border:1.4px solid rgba(255,255,255,.7);opacity:0;transform-origin:50% 50%;
  will-change:transform,opacity}

/* Same behaviour as a queue row: the glyph appears on hover, pure white, and the artwork
   is left alone the rest of the time. Larger here because the cover is larger. */
.np2-cover .ic{position:absolute;inset:0;display:grid;place-items:center;pointer-events:none;
  z-index:2;color:#fff;filter:drop-shadow(0 2px 9px rgba(0,0,0,.55));
  transition:opacity 300ms cubic-bezier(.2,0,0,1),transform 300ms cubic-bezier(.2,0,0,1),filter 300ms cubic-bezier(.2,0,0,1)}
.np2-cover .ic svg{width:46px;height:46px}
/* beats the shared .playing .ic-pause rule that drives the player button */
.np2-cover .ic-play,.np2-cover .ic-pause,
.np2-cover.playing .ic-play,.np2-cover.playing .ic-pause{opacity:0}
.np2-cover:hover .ic-play,.np2-cover:focus-visible .ic-play{opacity:1;transform:scale(1);filter:blur(0) drop-shadow(0 2px 10px rgba(0,0,0,.6))}
.np2-cover.playing:hover .ic-play,.np2-cover.playing:focus-visible .ic-play{opacity:0;transform:scale(.25)}
.np2-cover.playing:hover .ic-pause,.np2-cover.playing:focus-visible .ic-pause{opacity:1;transform:scale(1);filter:blur(0) drop-shadow(0 2px 10px rgba(0,0,0,.6))}
.np2-cover::after{content:'';position:absolute;inset:0;pointer-events:none;opacity:0;z-index:1;
  background:radial-gradient(circle at 50% 50%,rgba(10,20,35,.42),rgba(10,20,35,0) 70%);
  transition:opacity 220ms ease-out}
.np2-cover:hover::after,.np2-cover:focus-visible::after{opacity:1}

.np2-meta{display:flex;flex-direction:column}
/* the row keeps the chip's height even while the chip is hidden, so the block does not
   jump the first time the room is started */
.np2-top{display:flex;align-items:center;gap:10px;min-height:23px}
/* one scale for the block, the same treatment the player bar uses, a size up */
.np2-eyebrow{font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);opacity:1}
/* The state as a word in the line, with nothing around it. Green for playing, amber for paused -
   the convention the trade already reads: on a CDJ the play light is green and the cue light is
   amber, and red stays reserved for a fault. A paused room is not a fault. */
.statew{font-size:10px;letter-spacing:.18em;text-transform:uppercase;font-weight:700;
  margin-right:10px;white-space:nowrap;color:var(--muted)}
.statew[hidden]{display:none}
.statew.ok{color:#2fa76f}
.statew.stop,.statew.warn{color:#c88a1e}
[data-theme="dark"] .statew.ok{color:#5fd39a}
[data-theme="dark"] .statew.stop,[data-theme="dark"] .statew.warn{color:#e3b45a}
/* The notice ribbon: slides out from under the top bar, says its piece and slides back.
   Nothing on the dashboard reserves space for it, and it never stays on screen. */
.notice{position:fixed;left:50%;top:78px;z-index:40;
  display:inline-flex;align-items:center;gap:8px;max-width:min(620px,64vw);
  padding:8px 16px;border-radius:0 0 9px 9px;font-size:12.5px;line-height:1.35;text-align:center;
  color:var(--ink);background:rgba(255,255,255,.82);backdrop-filter:blur(14px) saturate(150%);
  -webkit-backdrop-filter:blur(14px) saturate(150%);
  box-shadow:0 6px 18px rgba(20,50,90,.16),inset 0 0 0 1px rgba(255,255,255,.6);
  opacity:0;pointer-events:none;
  transform:translateX(-50%) translateY(-140%);
  transition:opacity 260ms ease-out,transform 320ms cubic-bezier(.2,0,0,1)}
.notice.show{opacity:1;transform:translateX(-50%) translateY(0)}
.notice::before{content:'';width:7px;height:7px;border-radius:50%;background:var(--sun);flex:none}
[data-theme="dark"] .notice{background:rgba(18,28,42,.82);
  box-shadow:0 6px 18px rgba(0,0,0,.38),inset 0 0 0 1px rgba(255,255,255,.14)}
@media (prefers-reduced-motion: reduce){.notice{transition-duration:1ms}}
.np2-title{font-family:var(--font-display);font-size:21px;font-weight:700;line-height:1.1;margin-top:6px}
.np2-artist{font-size:13.5px;color:var(--muted);opacity:1;margin-top:4px}

/* ---- stage + grid ---- */
.stage{flex:1;min-height:0;overflow:auto;padding:2px 2px 4px}
/* On the pages without the hero the stage sat 2px under the top bar while the player sat 16 under
   the stage - the block read as shoved up against the bar. Same air on both sides now. */
.hero[hidden] ~ .stage{margin-top:14px}

/* ---- top bar (nav only) ---- */
/* the bar has to sit above the panels below it, or the notification list is drawn over */
.topbar{position:relative;z-index:50;flex:none;display:flex;align-items:center;justify-content:space-between;gap:16px;padding:13px 20px;border-radius:var(--r-2)}
.tb-l{display:flex;align-items:center;gap:24px}
/* Live text, not the outlined SVG: at 25px the font renderer hints the stems and the edges come
   out cleaner than any path can. The outlines stay in assets/brand for icons and for print. */
.word{font-family:var(--font-ui);font-size:27.5px;letter-spacing:.02em;font-weight:700;
  color:var(--ink);text-decoration:none;cursor:pointer;-webkit-font-smoothing:antialiased}
.word:hover{opacity:.75}
.word:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:4px}
.menu{display:flex;gap:4px}
.menu a{font-size:13.5px;font-weight:500;color:var(--muted);text-decoration:none;padding:9px 16px;border-radius:var(--r-1);cursor:pointer;transition:var(--t-fast)}
.menu a:hover{color:var(--ink);background:var(--surface-hover)}
.menu a.on{background:rgba(62,133,211,.16);color:var(--accent-deep);font-weight:500}
.tb-r{display:flex;align-items:center;gap:16px}
.live{font-size:11px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;
  display:flex;align-items:center;gap:7px;color:#c0392f;transition:color 200ms ease-out}
.live::before{content:'';width:8px;height:8px;border-radius:50%;background:currentColor;
  box-shadow:0 0 10px currentColor;transition:background 200ms ease-out}
.live.on{color:#1d9a5f}
.live.on::before{animation:pulse 2.4s ease-in-out infinite}
[data-theme="dark"] .live{color:#e8736a}
[data-theme="dark"] .live.on{color:#4fd08a}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
/* square, like every cover in the app - and it holds the venue's logo once there is one */
.ava{width:42px;height:42px;padding:0;border:none;border-radius:7px;
  background-size:cover;background-position:center;
  display:grid;place-items:center;color:#fff;font-size:17px;font-weight:700;letter-spacing:.02em;
  cursor:pointer;flex:none;transition:var(--t-fast)}
.ava:hover{box-shadow:0 0 0 3px rgba(62,133,211,.18)}
.ava.has-logo{border:1px solid rgba(20,40,70,.16)}
.venue-id{display:flex;flex-direction:column;align-items:flex-end;text-align:right;min-width:0}

/* ---- hero: artwork on the left, day and weather stacked on the right ---- */
.hero{position:relative;flex:none;display:flex;align-items:flex-end;justify-content:space-between;gap:24px;padding:17.5px 8px 24.5px;color:var(--ink)}   /* the artwork sits centred in the band:
   equal air above it to the top bar and below it to the panels */
/* display:flex outranks the hidden attribute, so without this the band claims to be gone and stays */
.hero[hidden]{display:none}
.hero-day,.hero-date,.hero-temp,.hero-cond,.hero-venue,.np2-eyebrow,.np2-title,.np2-artist{text-shadow:none}
.hero-day{font-family:var(--font-display);font-size:28px;font-weight:600;line-height:1;letter-spacing:.01em}
.hero-date{font-size:11px;letter-spacing:.2em;text-transform:uppercase;opacity:.85;margin-top:5px;white-space:nowrap}
.hero-r{display:flex;flex-direction:column;align-items:flex-end;text-align:right;align-self:flex-start;padding-right:16px}
/* the day on top, the weather under it, both hard to the right edge */
/* Icon and reading both sit ON the bottom edge of the artwork opposite - align to the baseline
   of the block, not its centre, or the words float half a line above the line they should meet. */
.hero-wx{display:flex;align-items:flex-end;justify-content:flex-end;gap:14px;margin-top:2px}
/* the condition reads under the temperature, not beside it */
.hero-wxtx{display:flex;flex-direction:column;align-items:center;justify-content:flex-end}
/* one ink, not the set's own palette: black on light, white on dark */
.hero-wx .wx{width:95px;height:95px;flex:none;display:block;
  filter:brightness(0) saturate(100%)}
[data-theme="dark"] .hero-wx .wx{filter:brightness(0) saturate(100%) invert(1)}
.hero-when{text-align:right}
.hero-temp{font-family:var(--font-display);font-size:44.9px;font-weight:600;line-height:1}
/* the block is anchored on its bottom, so widening this gap lifts the temperature and leaves
   the condition where it is */
.hero-cond{font-size:11px;letter-spacing:.16em;text-transform:uppercase;opacity:.9;
  margin-top:9px;text-align:center}
.hero-venue{font-size:11.5px;opacity:.8;margin-top:10px}

.np2{display:flex;align-items:flex-end;gap:18px}
/* the artwork IS the play control: press it to start or stop the room */
.np2-cover{position:relative;width:134px;height:134px;padding:0;border:none;cursor:pointer;flex:none;
  border-radius:7px;background-size:cover;background-position:center;overflow:hidden;
  box-shadow:0 16px 38px rgba(0,0,0,.42),inset 0 0 0 1px rgba(255,255,255,.18);
  transition:transform 150ms ease-out,box-shadow 200ms ease-out}
.np2-cover:hover{box-shadow:0 18px 44px rgba(0,0,0,.48),inset 0 0 0 1px rgba(255,255,255,.3)}
.np2-cover:active{transform:scale(.96)}
.np2-cover:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
/* the beat, felt on the outline: rings leave the artwork on every kick, the edge itself
   brightens with the low end, and the whole square breathes a fraction of a percent. */
.np2-art{position:relative;flex:none;line-height:0;transform-origin:50% 50%;will-change:transform}
.np2-edge,.np2-ring{position:absolute;inset:0;border-radius:7px;pointer-events:none}
.np2-edge{border:1px solid rgba(255,255,255,.16);z-index:3}
.np2-ring{border:1.4px solid rgba(255,255,255,.7);opacity:0;transform-origin:50% 50%;
  will-change:transform,opacity}

/* Same behaviour as a queue row: the glyph appears on hover, pure white, and the artwork
   is left alone the rest of the time. Larger here because the cover is larger. */
.np2-cover .ic{position:absolute;inset:0;display:grid;place-items:center;pointer-events:none;
  z-index:2;color:#fff;filter:drop-shadow(0 2px 9px rgba(0,0,0,.55));
  transition:opacity 300ms cubic-bezier(.2,0,0,1),transform 300ms cubic-bezier(.2,0,0,1),filter 300ms cubic-bezier(.2,0,0,1)}
.np2-cover .ic svg{width:46px;height:46px}
/* beats the shared .playing .ic-pause rule that drives the player button */
.np2-cover .ic-play,.np2-cover .ic-pause,
.np2-cover.playing .ic-play,.np2-cover.playing .ic-pause{opacity:0}
.np2-cover:hover .ic-play,.np2-cover:focus-visible .ic-play{opacity:1;transform:scale(1);filter:blur(0) drop-shadow(0 2px 10px rgba(0,0,0,.6))}
.np2-cover.playing:hover .ic-play,.np2-cover.playing:focus-visible .ic-play{opacity:0;transform:scale(.25)}
.np2-cover.playing:hover .ic-pause,.np2-cover.playing:focus-visible .ic-pause{opacity:1;transform:scale(1);filter:blur(0) drop-shadow(0 2px 10px rgba(0,0,0,.6))}
.np2-cover::after{content:'';position:absolute;inset:0;pointer-events:none;opacity:0;z-index:1;
  background:radial-gradient(circle at 50% 50%,rgba(10,20,35,.42),rgba(10,20,35,0) 70%);
  transition:opacity 220ms ease-out}
.np2-cover:hover::after,.np2-cover:focus-visible::after{opacity:1}

.np2-meta{display:flex;flex-direction:column}
/* the row keeps the chip's height even while the chip is hidden, so the block does not
   jump the first time the room is started */
.np2-top{display:flex;align-items:center;gap:10px;min-height:23px}
/* one scale for the block, the same treatment the player bar uses, a size up */
.np2-eyebrow{font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);opacity:1}
/* The state as a word in the line, with nothing around it. Green for playing, amber for paused -
   the convention the trade already reads: on a CDJ the play light is green and the cue light is
   amber, and red stays reserved for a fault. A paused room is not a fault. */
.statew{font-size:10px;letter-spacing:.18em;text-transform:uppercase;font-weight:700;
  margin-right:10px;white-space:nowrap;color:var(--muted)}
.statew[hidden]{display:none}
.statew.ok{color:#2fa76f}
.statew.stop,.statew.warn{color:#c88a1e}
[data-theme="dark"] .statew.ok{color:#5fd39a}
[data-theme="dark"] .statew.stop,[data-theme="dark"] .statew.warn{color:#e3b45a}
/* The notice ribbon: slides out from under the top bar, says its piece and slides back.
   Nothing on the dashboard reserves space for it, and it never stays on screen. */
.notice{position:fixed;left:50%;top:78px;z-index:40;
  display:inline-flex;align-items:center;gap:8px;max-width:min(620px,64vw);
  padding:8px 16px;border-radius:0 0 9px 9px;font-size:12.5px;line-height:1.35;text-align:center;
  color:var(--ink);background:rgba(255,255,255,.82);backdrop-filter:blur(14px) saturate(150%);
  -webkit-backdrop-filter:blur(14px) saturate(150%);
  box-shadow:0 6px 18px rgba(20,50,90,.16),inset 0 0 0 1px rgba(255,255,255,.6);
  opacity:0;pointer-events:none;
  transform:translateX(-50%) translateY(-140%);
  transition:opacity 260ms ease-out,transform 320ms cubic-bezier(.2,0,0,1)}
.notice.show{opacity:1;transform:translateX(-50%) translateY(0)}
.notice::before{content:'';width:7px;height:7px;border-radius:50%;background:var(--sun);flex:none}
[data-theme="dark"] .notice{background:rgba(18,28,42,.82);
  box-shadow:0 6px 18px rgba(0,0,0,.38),inset 0 0 0 1px rgba(255,255,255,.14)}
@media (prefers-reduced-motion: reduce){.notice{transition-duration:1ms}}
.np2-title{font-family:var(--font-display);font-size:21px;font-weight:700;line-height:1.1;margin-top:6px}
.np2-artist{font-size:13.5px;color:var(--muted);opacity:1;margin-top:4px}

/* ---- stage + grid ---- */
.stage{flex:1;min-height:0;overflow:auto;padding:2px 2px 4px}
/* On the pages without the hero the stage sat 2px under the top bar while the player sat 16 under
   the stage - the block read as shoved up against the bar. Same air on both sides now. */
.hero[hidden] ~ .stage{margin-top:14px}

.view.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:18px;align-content:start}
.view[hidden]{display:none!important}
.cat-head{margin-bottom:20px}
.cat-head .search{width:100%;max-width:560px}
.tile{padding:20px 22px;border-radius:var(--r-2)}
.scenes-tile{grid-column:span 5}
.energy-tile{grid-column:span 3}
.upnext-tile{grid-column:span 4}
@media(max-width:1180px){.view.grid{grid-template-columns:1fr 1fr}.scenes-tile,.energy-tile,.upnext-tile{grid-column:auto}}
@media(max-width:700px){.view.grid{grid-template-columns:1fr}}

.mono{position:absolute;inset:0;display:grid;place-items:center;font-family:var(--font-display);font-weight:600;color:rgba(255,255,255,.55);pointer-events:none;z-index:1}
.cat-cover .mono{font-size:48px}

/* scenes tile */
.h{font-family:var(--font-display);font-size:15px;font-weight:700;margin:0}
.sub{font-size:12px;color:var(--muted);margin:4px 0 16px}
.scenes{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.scene{padding:14px 16px;border-radius:var(--r-2);background:rgba(255,255,255,.34);cursor:pointer;transition:var(--t-fast);border:1px solid rgba(255,255,255,.5)}
.scene:hover{background:rgba(255,255,255,.52)}
.scene .time{font-size:11px;color:var(--muted)}.scene .n{font-family:var(--font-display);font-weight:600;font-size:16px;margin:2px 0 4px}.scene .d{font-size:11px;color:var(--muted)}
.scene.active{background:rgba(62,133,211,.14);border:1px solid rgba(62,133,211,.4)}
.badge{float:right;font-size:9px;letter-spacing:.1em;text-transform:uppercase;font-weight:600;color:#fff;background:var(--accent);padding:4px 10px;border-radius:4px}

/* energy + zones */
.srow{display:flex;justify-content:space-between;align-items:baseline;gap:16px}
.val{font-family:var(--font-display);font-weight:600;font-size:22px;color:var(--accent-deep)}
.slider{margin-top:14px;height:10px;border-radius:var(--r-pill);position:relative;cursor:pointer;background:rgba(38,51,63,.14);box-shadow:inset 0 1px 3px rgba(40,64,96,.14)}
.fillbar{position:absolute;left:0;top:0;height:100%;width:58%;border-radius:var(--r-pill);background:linear-gradient(90deg,var(--accent),var(--accent-glow,#7fb0e6))}
.knob{position:absolute;top:50%;left:58%;width:22px;height:22px;border-radius:50%;transform:translate(-50%,-50%);background:radial-gradient(circle at 38% 34%,#fff,var(--accent));box-shadow:0 3px 9px rgba(62,133,211,.5),inset 0 1px 1px rgba(255,255,255,.9)}
.ends{display:flex;justify-content:space-between;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-top:8px}
.zones{display:flex;gap:10px;margin-top:2px}
.chip{font-size:12.5px;padding:9px 16px;border-radius:var(--r-1);cursor:pointer;background:var(--surface);border:1px solid var(--surface-brd);transition:var(--t-fast)}
.chip.on{background:rgba(62,133,211,.9);color:#fff;border-color:transparent}

/* up next */
.q{display:flex;align-items:center;gap:14px;padding:9px 8px;border-radius:var(--r-2);transition:var(--t-fast)}
.q:hover{background:var(--surface-hover)}
.qc{width:42px;height:42px;border-radius:8px;flex:none;cursor:pointer;box-shadow:0 5px 14px rgba(60,90,130,.18)}
.q .nm{font-size:14px;font-weight:600}.q .mt{font-size:11.5px;color:var(--muted);margin-top:1px}
.q .like{margin-left:auto}

/* catalogue: the two collection strips above the style grid */
.cat-groups{margin-top:38px}
.style-grid+.cat-groups{margin-top:38px}
.cat-h{margin:0 0 14px;font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);font-weight:600}
.ven-grid+.cat-h,.mom-grid+.cat-h{margin-top:38px}
/* a moment is a picture you point at: a square, the name on it, the reading underneath */
.mom-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(178px,1fr));gap:18px}
.mom{display:block;width:100%;cursor:pointer;background:none;border:0;padding:0;text-align:left;
  font-family:var(--font-ui)}
.mom-art{display:block;position:relative;aspect-ratio:1;border-radius:7px;overflow:hidden;
  box-shadow:0 12px 28px rgba(60,90,130,.20);border:1px solid rgba(255,255,255,.5)}
.mom-art .styl-mos{position:absolute;inset:0}
.mom-art::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(8,16,28,0) 38%,rgba(8,16,28,.72) 100%)}
.mom-name{display:block;position:absolute;left:12px;right:12px;bottom:10px;z-index:2;color:#fff;
  font-size:17px;font-weight:700;letter-spacing:-.01em;text-shadow:0 2px 10px rgba(0,0,0,.45)}
.mom:hover .mom-art{transform:translateY(-2px)}
.mom-art{transition:transform var(--t-fast),box-shadow var(--t-fast)}
.mom-meta{display:block;margin-top:7px;font-size:11.5px;color:var(--muted)}

/* a kind of venue is a filing label: wide, short, the thumbnail on the right */
.ven-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(244px,1fr));gap:14px}
/* Each kind of room carries its own colour properly - a real tint across the whole tile, not the
   little accent stripe every generated interface wears down its left edge. The artwork runs the
   full height, flush to the edge, so the tile is one object rather than a card with an island on
   it. */
.ven{display:flex;align-items:stretch;gap:0;padding:0;cursor:pointer;overflow:hidden;
  border-radius:3px;border:1px solid var(--vc);background:var(--vt);
  font-family:var(--font-ui);text-align:left;transition:var(--t-fast)}
.ven:hover{background:var(--vh);transform:translateY(-1px)}
.ven-tx{display:block;flex:1;min-width:0;padding:18px 16px}
.ven-name{display:block;font-size:13.5px;font-weight:600;color:var(--vf)}
.ven-note{display:block;font-size:10.5px;color:var(--vf);opacity:.74;margin-top:4px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ven-art{display:block;width:74px;flex:none;position:relative;align-self:stretch;overflow:hidden}
.ven-art .styl-mos{position:absolute;inset:0}

/* catalogue */
.search{font-family:var(--font-ui);font-size:13px;padding:10px 16px;border-radius:var(--r-1);border:1px solid var(--surface-brd);background:var(--surface);color:var(--ink);min-width:250px;outline:none}
.search::placeholder{color:var(--muted)}
.cat-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:20px}
.cat-cover{aspect-ratio:1;border-radius:7px;position:relative;overflow:hidden;cursor:pointer;box-shadow:0 12px 28px rgba(60,90,130,.2);border:1px solid rgba(255,255,255,.5)}
.cat-cover .like{position:absolute;top:8px;right:10px;z-index:2;color:#fff;font-size:15px;text-shadow:0 1px 5px rgba(0,0,0,.4)}
.cat-play{position:absolute;inset:0;margin:auto;width:50px;height:50px;border-radius:50%;display:grid;place-items:center;background:rgba(255,255,255,.6);backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.85);color:var(--accent-deep);opacity:0;transition:var(--t-fast);pointer-events:none}
.cat-play svg{width:19px;height:19px;display:block;margin-left:2px}
.cat-cover:hover .cat-play{opacity:1}
.cat-title{font-size:13.5px;font-weight:600;margin-top:10px}
.cat-meta{font-size:11.5px;color:var(--muted);margin-top:1px}
.cat-mood{display:inline-block;margin-top:7px;font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);background:rgba(255,255,255,.55);padding:3px 9px;border-radius:5px}
.like{font-family:var(--font-ui);background:none;border:none;cursor:pointer;font-size:17px;line-height:0;color:var(--muted);transition:var(--t-fast);padding:2px}
.like svg{width:1em;height:1em;display:block;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linejoin:round;stroke-linecap:round}
.like.on svg{fill:currentColor;stroke:none}
.like:hover{color:var(--accent);transform:scale(1.12)}.like.on{color:var(--accent)}


.ripple{position:absolute;border-radius:50%;transform:translate(-50%,-50%) scale(0);pointer-events:none;background:radial-gradient(circle,rgba(255,255,255,.7)0,transparent 70%);animation:rip .6s ease-out forwards}
@keyframes rip{to{transform:translate(-50%,-50%) scale(1);opacity:0}}

/* thin cursor-only scrollbars (no track, no arrows) */
*{scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.28) transparent}
*::-webkit-scrollbar{width:4px;height:4px}
*::-webkit-scrollbar-track{background:transparent}
*::-webkit-scrollbar-thumb{background:rgba(0,0,0,.28);border-radius:999px}
*::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.42)}
*::-webkit-scrollbar-button{display:none;width:0;height:0}
*::-webkit-scrollbar-corner{background:transparent}

/* venue setup summary (read-only reflection of settings) */
.sum{margin-top:8px}
.sum-row{display:flex;justify-content:space-between;gap:14px;padding:11px 0;border-bottom:1px solid var(--hairline);font-size:13px}
.sum-row:last-child{border-bottom:none}
.sum-row span{color:var(--muted)}
.sum-row b{font-weight:600;text-align:right}

/* google-calendar style schedule */
.cal{flex:1;overflow:auto;margin-top:12px}
.cal-head{display:grid;position:sticky;top:0;z-index:2;padding-bottom:6px}
.cal-dh{text-align:center;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);font-weight:600;padding:5px 0}
.cal-dh.today{color:var(--accent-deep)}
.cal-body{display:grid}
.cal-gutter{display:flex;flex-direction:column}
.cal-h{height:44px;font-size:10px;color:var(--muted);text-align:right;padding-right:7px;transform:translateY(-6px)}
.cal-col{position:relative;border-left:1px solid var(--hairline);background:repeating-linear-gradient(to bottom,transparent 0,transparent 43px,var(--hairline) 43px,var(--hairline) 44px)}
.cal-col.today{background:linear-gradient(rgba(62,133,211,.07),rgba(62,133,211,.07)),repeating-linear-gradient(to bottom,transparent 0,transparent 43px,var(--hairline) 43px,var(--hairline) 44px)}
.cal-ev{position:absolute;left:3px;right:3px;border-radius:6px;padding:5px 8px;color:#fff;overflow:hidden;box-shadow:0 3px 8px rgba(0,0,0,.16)}
.cal-ev b{font-size:11.5px;font-weight:600;display:block;line-height:1.2;text-shadow:0 1px 2px rgba(0,0,0,.25)}
.cal-ev span{font-size:10px;opacity:.92;text-shadow:0 1px 2px rgba(0,0,0,.25)}
.cal-now{position:absolute;left:0;right:0;height:2px;background:#E5794C;z-index:3}
.cal-now::before{content:'';position:absolute;left:-3px;top:-3px;width:8px;height:8px;border-radius:50%;background:#E5794C}

/* ---- energy / mood temperature (used by the gauge panel) ---- */
.nrg-cells{position:relative;display:flex;gap:2px;height:16px;align-items:stretch}
.nrg-cell{flex:1;min-width:0;border-radius:2px;opacity:.2;transition:opacity .07s linear}
.nrg-cell.on{opacity:1}
.nrg-mark{position:absolute;top:-3px;bottom:-3px;width:2px;border-radius:2px;background:var(--ink);transform:translateX(-50%);pointer-events:none;transition:left .6s var(--ease);z-index:2}
.nrg-mark::before{content:'';position:absolute;top:-3px;left:50%;transform:translateX(-50%);width:9px;height:9px;border-radius:50%;background:var(--ink);box-shadow:0 0 0 2px var(--glass-bg)}
.nrg-txt{font-size:11px;margin-top:8px;opacity:.92;text-shadow:0 1px 6px rgba(0,0,0,.55)}

/* ---- 3-column dashboard ---- */
.view.cols{display:grid;grid-template-columns:1fr 1fr;gap:18px;height:100%}
.view.cols .tile{display:flex;flex-direction:column;min-height:0}
@media(max-width:1100px){.view.cols{grid-template-columns:1fr;height:auto}}
.col-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
.tag{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);background:var(--surface);border:1px solid var(--surface-brd);padding:4px 10px;border-radius:5px;white-space:nowrap}

/* playlist queue */
.queue{flex:1;overflow:auto;margin-top:8px;padding-right:4px}
.qrow{display:flex;align-items:center;gap:12px;padding:8px;border-radius:10px;cursor:pointer;transition:var(--t-fast)}
.qrow:hover{background:var(--surface-hover)}
.qrow.now{background:rgba(62,133,211,.15);border:1px solid rgba(62,133,211,.32)}
.qc2{width:60px;height:60px;border-radius:4.5px;flex:none;background-size:cover;background-position:center;box-shadow:0 5px 14px rgba(60,90,130,.2);position:relative}
.qplay{position:absolute;inset:0;display:grid;place-items:center;background:none;border:none;cursor:pointer;color:rgba(255,255,255,.8);filter:drop-shadow(0 1px 5px rgba(0,0,0,.55));transition:var(--t-fast)}
.qplay svg{width:32px;height:32px;display:block}
.qplay:hover{color:#fff;transform:scale(1.08)}
.qrow .like{font-size:16px}
.qtx{flex:1;min-width:0}
.qrow .nm{font-size:13.5px;font-weight:600;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.qrow .mt{font-size:11px;color:var(--muted);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.qrow .al{font-size:10.5px;color:var(--muted);opacity:.75;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.qi{margin-left:auto;display:flex;align-items:center;gap:10px}
.qdot{width:9px;height:9px;border-radius:50%;flex:none}

/* owner settings */
.set-block{margin-top:18px}
.lab{font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.seg{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px}
.seg span{font-size:12.5px;padding:9px 14px;border-radius:var(--r-1);background:var(--surface);border:1px solid var(--surface-brd);cursor:pointer;transition:var(--t-fast);user-select:none}
.seg span:hover{background:var(--surface-hover)}
.seg span.on{background:rgba(62,133,211,.14);border-color:var(--accent);color:var(--accent-deep);font-weight:600;box-shadow:inset 0 0 0 1px var(--accent)}
[data-theme="dark"] .seg span.on{color:var(--accent-glow)}
.seg.small span{font-size:11px;padding:6px 11px}
.toggles{display:flex;flex-direction:column;gap:12px}
.tog{display:flex;justify-content:space-between;align-items:center;cursor:pointer;font-size:13px}
.tog i{width:40px;height:22px;border-radius:var(--r-pill);background:var(--track);position:relative;transition:var(--t-fast);flex:none}
.tog i::after{content:'';position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;transition:var(--t-fast);box-shadow:0 1px 3px rgba(0,0,0,.25)}
.tog.on i{background:var(--accent)}
.tog.on i::after{left:20px}
.license{margin-top:auto;padding-top:16px;font-size:10.5px;color:var(--muted);line-height:1.5}

/* schedule graph */
.sch-wrap{flex:1;min-height:150px;margin-top:10px}
#sch{width:100%;height:100%;display:block;overflow:visible}
.sch-anchor{cursor:ns-resize}
.sch-now{font-size:12px;margin-top:10px;color:var(--ink)}
.ch-r{display:flex;align-items:center;gap:8px}
.gear{background:var(--surface);border:1px solid var(--surface-brd);width:30px;height:30px;border-radius:8px;cursor:pointer;font-size:14px;color:var(--ink);transition:var(--t-fast)}
.gear:hover{color:var(--accent-deep);background:var(--surface-hover)}

/* venue name in top bar */
.venue-name{text-align:right;line-height:1.15}
.vn{font-family:var(--font-ui);font-size:14px;font-weight:600;display:block}
.vl{font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}

/* buttons */
.btn{font-size:12.5px;padding:9px 18px;border-radius:var(--r-1);border:1px solid var(--accent);cursor:pointer;background:var(--accent);color:#fff;font-family:var(--font-ui);font-weight:600;position:relative;overflow:hidden;transition:var(--t-fast)}
.btn:hover{filter:brightness(1.06)}
.btn.ghost{background:var(--surface);color:var(--ink);border:1px solid var(--surface-brd);font-weight:500}
.btn.ghost:hover{background:var(--surface-hover);border-color:rgba(62,133,211,.5)}
.btn.danger{background:rgba(200,70,60,.12);color:#b8463a;border:1px solid rgba(200,70,60,.45);font-weight:600}
.open-settings{width:100%;margin-top:20px}

/* settings view */
.set-view{display:grid;grid-template-columns:230px 1fr;gap:18px;height:100%}
@media(max-width:900px){.set-view{grid-template-columns:1fr}}
.set-menu{padding:12px;border-radius:var(--r-2);display:flex;flex-direction:column;gap:3px;overflow:auto}
.set-menu a{display:flex;align-items:center;gap:10px;padding:10px 13px;border-radius:9px;
  font-size:13.5px;color:var(--ink);cursor:pointer;transition:var(--t-fast)}
.set-menu a svg{width:17px;height:17px;flex:none;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;opacity:.75}
.set-menu a.on svg{opacity:1}
/* the two jobs, told apart by a label and a rule rather than by two screens */
.set-group{font-size:9.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
  padding:4px 13px 6px;font-weight:600}
.set-group-2{margin-top:10px;padding-top:12px;border-top:1px solid var(--hairline)}
.set-menu a:hover{background:var(--surface-hover)}
.set-menu a.on{background:rgba(62,133,211,.16);color:var(--accent-deep);font-weight:500}
.set-content{overflow:auto;padding-right:4px}
.set-sec{display:none}
.set-sec.on{display:block}
.set-card{padding:22px 24px;border-radius:var(--r-2);margin-bottom:16px}
.set-title{font-family:var(--font-display);font-size:22px;font-weight:700;margin:0 0 4px}
.set-sub{font-size:12px;color:var(--muted);margin:0 0 16px}
.field-lab{font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin:16px 0 7px;display:block}
.field{width:100%;font-family:var(--font-ui);font-size:13px;padding:10px 14px;border-radius:var(--r-1);border:1px solid var(--surface-brd);background:var(--surface);color:var(--ink);outline:none}
.field:focus{border-color:var(--accent)}
/* A dropdown has to look like the fields around it, arrow included - the browser's own control
   sits a good two pixels taller than everything else on the card otherwise. */
select.field.sel{appearance:none;-webkit-appearance:none;cursor:pointer;padding-right:34px;
  background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),linear-gradient(135deg,var(--muted) 50%,transparent 50%);
  background-position:calc(100% - 18px) 51%,calc(100% - 13px) 51%;background-size:5px 5px,5px 5px;background-repeat:no-repeat}

/* The thirteen rooms at first registration: every one visible, none of them a dropdown. A grid
   rather than a wrapped row because thirteen names of very different lengths wrap into a ragged
   paragraph, and this is the answer the whole schedule hangs on - it deserves to be read. */
.vgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:7px;margin-top:9px}
.vgrid span{display:flex;align-items:center;gap:8px;font-size:12.5px;line-height:1.25;
  padding:11px 13px;border-radius:var(--r-1);background:var(--surface);border:1px solid var(--surface-brd);
  cursor:pointer;transition:var(--t-fast);user-select:none}
.vgrid span:hover{background:var(--surface-hover);border-color:var(--vc)}
.vgrid .vdot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;opacity:.55;transition:var(--t-fast)}
.vgrid span.on{border-color:var(--vc);box-shadow:inset 0 0 0 1px var(--vc);font-weight:600;
  background:color-mix(in srgb,var(--vc) 12%,var(--surface))}
.vgrid span.on .vdot{opacity:1;transform:scale(1.15)}

textarea.field{resize:vertical}
.two{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.set-row{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:13px 0;border-bottom:1px solid var(--hairline)}
.set-row:last-child{border-bottom:none}
.rowsub{font-size:11.5px;color:var(--muted);margin-top:2px}
.chev{color:var(--muted);font-size:18px}
.plan{display:flex;justify-content:space-between;align-items:flex-end;gap:20px;padding:18px 20px;
  border-radius:var(--r-2);background:rgba(62,133,211,.1);margin-bottom:10px}
.plan-badge{display:inline-block;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;
  color:#fff;background:var(--accent);padding:4px 10px;border-radius:4px}
.plan-name{font-family:var(--font-display);font-weight:700;font-size:20px;margin-top:9px}
/* the amount and the period are different sizes on purpose - the rule that shrank every span
   inside was catching the number itself, which is why 29 came out smaller than the euro sign */
.plan-price{display:flex;flex-direction:column;align-items:flex-end;gap:2px;color:var(--accent-deep)}
.plan-price .amt{font-family:var(--font-display);font-weight:700;font-size:34px;line-height:1;
  font-variant-numeric:tabular-nums}
.plan-price .pm{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);font-weight:600}

/* ---------------------------------------------------------------- admin
   Ours, and it says so: the fourth item in a bar whose other three belong to the venue. Coloured
   for that reason and no other - it is the one link in the app that leads somewhere a customer
   never goes. */
.menu a.tab-admin{color:#C08A3E;font-weight:600}
.menu a.tab-admin::before{content:'';display:inline-block;width:5px;height:5px;border-radius:50%;
  background:#C08A3E;margin-right:6px;vertical-align:middle;opacity:.9}
.menu a.tab-admin.on{background:rgba(192,138,62,.14);color:#C08A3E}

.ad-head{margin-bottom:18px}
.ad-head h1{margin:0 0 6px;font-size:22px;font-weight:700;letter-spacing:-.01em}
.ad-head .sub{margin:0;max-width:70ch}

.ad-grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(270px,1fr))}
.ad-card{background:var(--surface);border:1px solid var(--surface-brd);border-radius:var(--r-3);
  padding:18px 20px;text-decoration:none;color:inherit;display:block}
.ad-card h2{margin:0;font-size:15.5px;font-weight:700}
.ad-card p{margin:9px 0 0;font-size:12.5px;line-height:1.6;color:var(--muted)}
/* not `.live` - that class already means the LIVE state pill in this app, and borrowing it
   set the whole card in uppercase letter-spaced red */
.ad-card.ad-open{transition:var(--t-fast)}
.ad-card.ad-open:hover{border-color:var(--accent);background:var(--surface-hover)}
.ad-card.soon{opacity:.62}
.ad-row{display:flex;align-items:center;gap:10px}
.ad-dot{width:8px;height:8px;border-radius:50%;background:var(--muted);margin-left:auto;
  transition:var(--t-fast)}
.ad-dot.up{background:#4E9A5E;box-shadow:0 0 0 4px rgba(78,154,94,.16)}
.ad-dot.down{background:#C08A3E;box-shadow:0 0 0 4px rgba(192,138,62,.14)}
.ad-where{font-family:var(--font-mono,ui-monospace,monospace);font-size:11.5px;color:var(--accent)}
.ad-card.soon .ad-where{font-family:inherit;color:var(--muted)}
/* one fact per line, so the answer to "which catalogue is this room on" is readable at a glance */
.ad-kv{display:flex;justify-content:space-between;gap:16px;margin-top:8px !important;
  border-top:1px solid var(--surface-brd);padding-top:8px}
.ad-kv span{color:var(--muted)}
.ad-kv b{font-weight:600;color:var(--ink);text-align:right}
