/* ===================================
   THEME
   PiKaraoke's palette, said in Bulma's own variables. Bulma derives every
   component's colours from these.
   =================================== */
:root {
  /* Bulma applies the family to body and to every form control. */
  --bulma-family-primary: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue",
    sans-serif;

  /* The neutral ramp. Every page's content sits in one box, so the box is Bulma's
     scheme-main: the surface Bulma measures its stripes, hovers, lines and borders
     from. The steps above it are Bulma's own dark-theme offsets, counted from the
     box, so moving the box moves all of them. The page around the box is the one
     surface beyond Bulma's ramp. Bulma keeps text on its own hue and saturation,
     so both pairs are set. */
  --bulma-scheme-h: 182;
  --bulma-scheme-s: 7%;
  --bulma-text-h: 182deg;
  --bulma-text-s: 7%;
  --bulma-scheme-main-l: 22%;
  --bulma-scheme-main-bis-l: calc(var(--bulma-scheme-main-l) + 2%);
  --bulma-scheme-main-ter-l: calc(var(--bulma-scheme-main-l) + 4%);
  --bulma-background-l: calc(var(--bulma-scheme-main-l) + 5%);
  --bulma-border-weak-l: calc(var(--bulma-scheme-main-l) + 12%);
  --bulma-border-l: calc(var(--bulma-scheme-main-l) + 15%);
  --bulma-body-background-color:
    hsl(var(--bulma-scheme-h), var(--bulma-scheme-s), calc(var(--bulma-scheme-main-l) - 9%));
  --bulma-text-weak-l: 58%;
  --bulma-text-l: 87%;
  --bulma-text-strong-l: 95%;

  --bulma-primary-h: 211deg;
  --bulma-primary-s: 40%;
  --bulma-primary-l: 36%;
  --bulma-link-h: 168deg;
  --bulma-link-s: 76%;
  --bulma-link-l: 42%;
  --bulma-info-h: 204deg;
  --bulma-info-s: 71%;
  --bulma-info-l: 53%;
  /* Add-to-queue's colour on every page: a soft true green, clear of the link teal. */
  --bulma-success-h: 128deg;
  --bulma-success-s: 45%;
  --bulma-success-l: 52%;
  --bulma-warning-h: 45deg;
  --bulma-warning-s: 89%;
  --bulma-warning-l: 50%;
  --bulma-danger-h: 6deg;
  --bulma-danger-s: 78%;
  --bulma-danger-l: 57%;

  /* Bulma picks each accent's ink from a lightness ladder compiled for its own
     accent, and does not recompute it when the accent changes. Its primary is a
     luminous turquoise wanting near-black ink; this navy wants white, which is
     7.2:1 where the baked ink is 2.7:1. The same mismatch runs the other way on
     the teal, where dark ink replaces white at 2.4:1. */
  --bulma-primary-invert-l: var(--bulma-primary-100-l);
  --bulma-link-invert-l: var(--bulma-link-05-l);
  /* Text in an accent colour is re-lit to stay readable on the page. The navy
     needs lifting (5.2:1). Links keep the palette teal as it is, so the hover, 5%
     lighter, lands on the palette's hover teal exactly. */
  --bulma-primary-on-scheme-l: var(--bulma-primary-60-l);
  --bulma-link-on-scheme-l: var(--bulma-link-l);

  /* Inset panels - has-background-dark, cards, the now-playing row - sit one of
     Bulma's 5% steps below the box, on the ramp's hue rather than Bulma's blue. */
  --bulma-dark-h: var(--bulma-scheme-h);
  --bulma-dark-s: var(--bulma-scheme-s);
  --bulma-dark-l: calc(var(--bulma-scheme-main-l) - 5%);

  /* Bulma's dark theme moves a hovered colour 5%, too faint on this ramp to read as a
     hover; every hover in the app takes its step from these two. */
  --bulma-hover-color-l-delta: 10%;
  --bulma-hover-background-l-delta: 10%;

  /* Surfaces are told apart by their fill, not by Bulma's dark-theme glow. */
  --bulma-shadow: none;

  /* One corner radius for every surface and control. */
  --bulma-radius-small: var(--bulma-radius);
  --bulma-radius-medium: var(--bulma-radius);
  --bulma-radius-large: var(--bulma-radius);

  /* Bulma scopes the list-item hover to .panel; the option lists in the modals are
     panel-blocks too, and hover the same way. */
  --bulma-panel-block-hover-background-color: var(--bulma-background);
}

/* Plain buttons are dark wells, below the page as on master. Bulma fills them with
   the surface they sit on and outlines them to compensate; filled, they need no
   outline, like the coloured buttons. is-outlined sets its own width. Disabled,
   they keep their fill and Bulma's opacity fades it; Bulma's own disabled fill is
   scheme-main, the box itself, where a faded button vanishes. */
.button {
  --bulma-button-background-l: calc(var(--bulma-scheme-main-l) - 13%);
  --bulma-button-border-width: 0px;
  --bulma-button-disabled-background-color:
    hsl(var(--bulma-scheme-h), var(--bulma-scheme-s), var(--bulma-button-background-l));
}

/* Hovering lights the label; the fill lifts only as far as master's, short of
   fading into the box. Coloured buttons keep Bulma's hover. */
.button:hover:not(
    [disabled],
    .is-white,
    .is-black,
    .is-light,
    .is-dark,
    .is-primary,
    .is-link,
    .is-info,
    .is-success,
    .is-warning,
    .is-danger,
    .is-text,
    .is-ghost
  ) {
  --bulma-button-background-l-delta: 5%;
  color: var(--bulma-link-text-hover);
}

/* Cards are inset panels. A card's header is set apart from its body by fill, the
   page colour, as a modal's is below. */
.card {
  --bulma-card-background-color: var(--bulma-dark);
  --bulma-card-header-background-color: var(--bulma-body-background-color);
  --bulma-card-radius: var(--bulma-radius);
}

/* The join card sets the QR code beside its web address on a wide screen. On a
   phone that squeezes the address into a sliver, so stack them instead - code
   above, address centred below. Bulma has no responsive flex-direction helper. */
@media screen and (max-width: 768px) {
  .join-card {
    flex-direction: column;
    text-align: center;
  }
  .join-card > div {
    margin-inline-start: 0;
    margin-top: 1rem;
  }
}

/* Every data-table cell centres on its row, so an icon, a tag or a two-line wrap
   all sit level against the text. Bulma top-aligns by default; this one rule
   replaces the per-cell is-vcentered the row generators used to carry. */
.table td {
  vertical-align: middle;
}

/* Rows are told apart by the stripe, so the cell border is off. Bulma's stripe,
   a step stronger. Any hovered row takes the app's hover step. */
.table {
  --bulma-table-cell-border-width: 0;
  --bulma-table-striped-row-even-background-color: var(--bulma-scheme-main-ter);
  --bulma-table-row-hover-background-color:
    hsl(var(--bulma-scheme-h), var(--bulma-scheme-s),
      calc(var(--bulma-scheme-main-l) + var(--bulma-hover-background-l-delta)));
  --bulma-table-striped-row-even-hover-background-color: var(--bulma-table-row-hover-background-color);
}

/* Bulma sets a modal's header apart from its body only by the shadow turned off
   above; the page colour does it by fill, as the wells do. */
.modal {
  --bulma-modal-card-head-background-color: var(--bulma-body-background-color);
}

/* Every modal card is a short option list. */
.modal-card {
  --bulma-modal-card-head-padding: 1rem;
  --bulma-modal-card-body-padding: 0;
  width: 90%;
  max-width: 400px;
}
.modal-card-title {
  max-width: 95%;
}

/* Bulma rounds the foot but not the body, so a footless modal ends square. */
.modal-card-body:last-child {
  border-end-start-radius: var(--bulma-modal-card-foot-radius);
  border-end-end-radius: var(--bulma-modal-card-foot-radius);
}

/* The text-prompt dialog is a form, not the self-padding option list the shared
   style zeroes body padding for, so its message and input get the head's inset. */
#text-prompt-modal .modal-card {
  --bulma-modal-card-body-padding: 1rem;
}
/* Space the Save and Cancel buttons apart; the foot gives them none. */
#text-prompt-modal .modal-card-foot {
  gap: 0.75rem;
}

/* Bulma 1.0 defines a link hover colour but applies it to no plain link, only to its
   own components, which hover their own way. This is the rule bulma-dark.css used
   to carry, for text links only. */
a:not(.button, .navbar-item, .navbar-link, .navbar-burger, .panel-block, .delete):hover {
  color: var(--bulma-link-text-hover);
}

/* Form fields stay white on the dark page, so the colours Bulma derives for them
   from the dark scheme - text, placeholder and icon, all near-white - are taken
   from the field instead. The placeholder is the field's own text at partial
   alpha, the same pattern Bulma uses for it. */
.control,
.input,
.select,
.textarea {
  --bulma-input-background-l: var(--bulma-white-l);
  --bulma-input-color-l: var(--bulma-dark-l);
  --bulma-input-placeholder-color:
    hsla(var(--bulma-input-h), var(--bulma-input-s), var(--bulma-input-color-l), 0.7);
  --bulma-input-icon-color: var(--bulma-input-placeholder-color);
  --bulma-input-icon-hover-color: var(--bulma-input-placeholder-color);
}

/* Firefox applies its own default placeholder opacity on top of the colour. */
.input::placeholder,
.textarea::placeholder {
  opacity: 1;
}

/* A preference that applies on click is a switch, not a checkbox. Bulma has none,
   so it is drawn on a bare <input type="checkbox"> -- markup and :checked handlers
   unchanged -- with the thumb as the input's ::before, green when on. */
input.switch[type="checkbox"] {
  appearance: none;
  box-sizing: border-box;
  flex-shrink: 0;
  width: 2.5em;
  height: 1.4em;
  margin-right: 0.5em;
  border-radius: var(--bulma-radius-rounded);
  background-color: var(--bulma-border);
  vertical-align: middle;
  cursor: pointer;
  transition: background-color 0.2s ease;
}
input.switch[type="checkbox"]::before {
  content: "";
  display: block;
  width: calc(1.4em - 4px);
  height: calc(1.4em - 4px);
  margin: 2px;
  border-radius: var(--bulma-radius-rounded);
  background-color: var(--bulma-white);
  transition: transform 0.2s ease;
}
input.switch[type="checkbox"]:checked {
  background-color: var(--bulma-success);
}
input.switch[type="checkbox"]:checked::before {
  transform: translateX(calc(2.5em - 1.4em));
}
input.switch[type="checkbox"]:focus-visible {
  outline: 2px solid var(--bulma-link);
  outline-offset: 2px;
}

/* The heading row baseline-aligns its action so a text action sits on the title's
   baseline. A glyph action -- the autoplay switch, the bulk-rename icon -- has no
   such baseline, so it centres on the title and centres its own glyph on its label;
   the gap replaces the switch's default spacing. */
.heading-action {
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
}
.heading-action input.switch[type="checkbox"] {
  margin-right: 0;
}

/* ===================================
   ICONS (Phosphor)
   Every glyph is wrapped in Bulma's .icon, which flex-centres the glyph in its
   box; a vertical-align on .icon (below) then seats that box on the adjacent
   text -- so there is no bare <i class="ph"> to nudge and no per-glyph nudge.
   Size is the .icon tier, the glyph sitting a little
   inside its box so it reads a touch larger than Bulma's default.  Two bespoke
   exceptions keep their own sizing: .sort-arrow (a sub-em header indicator the
   1.5rem box is wrong for) and .singer-avatar (a themed slot, soon a photo).
   =================================== */
.ph:before {
  display: inline-block;
  width: 1em;
  text-align: center;
}
/* .icon is inline-flex, so on its own in a line it aligns to the text baseline
   and a box taller than the text rides high above it -- most visible on the
   add-song plus in a browse or rankings cell. Centre the box on the text's
   middle instead. Ignored wherever .icon is a flex item (buttons, navbar,
   panel-block, .icon-text, the rankings wrapper), so only bare inline icons move. */
.icon {
  vertical-align: middle;
}
.icon .ph {
  font-size: 1.25rem;
}
.icon.is-small .ph {
  font-size: 1rem;
}
.icon.is-medium .ph {
  font-size: 1.5rem;
}
.icon.is-large .ph {
  font-size: 2rem;
}
/* An icon embedded in sized display text -- the singer marker and the playback
   controls -- tracks that text's size rather than a fixed tier: the box hugs
   the glyph and the glyph inherits the surrounding font-size (is-size-3 for the
   controls, is-size-4 for the now-playing singer). */
.icon.is-text-size {
  width: auto;
  height: auto;
}
.icon.is-text-size .ph {
  font-size: 1em;
}

/* ===================================
   NAVBAR
   is-primary paints the bar, its items, the burger menu and the dropdown in the
   primary navy; these configure the rest.
   =================================== */
.navbar {
  border-radius: var(--bulma-radius);
}

.navbar.is-primary {
  /* The burger reads --bulma-navbar-burger-color, which defaults to the link
     colour; is-primary only sets a variable an overridden rule consumes. */
  --bulma-navbar-burger-color: var(--bulma-navbar-item-color);
  /* Lines sit the ramp's border step (+15%) lighter than their surface; the divider's
     surface is the bar's navy. Bulma's default is darker than the bar. */
  --bulma-navbar-divider-background-l: calc(var(--bulma-primary-l) + 15%);
  /* A text control: hover tints the text and holds the background, as every other
     text control in the app does. */
  --bulma-navbar-item-hover-background-l-delta: 0%;
}

/* One hamburger at every width. Bulma shows the brand burger only below 1024px and
   lays the secondary menu out inline above it; these rules keep the burger and its
   drop-down panel at all widths, so the menu looks and behaves the same on a phone
   and a desktop. The panel's chrome (navy background, shadow, padding) is Bulma's
   own mobile styling, which the desktop breakpoint leaves untouched. */
.navbar-menu.is-active {
  position: absolute;
  right: 0;
}
@media screen and (min-width: 1024px) {
  /* Block, not flex: the brand row then fills the bar's width, so the burger's
     margin-inline-start:auto carries it to the far right and the panel drops
     below it - exactly the mobile layout. */
  .navbar {
    display: block;
  }
  .navbar-burger {
    display: inline-flex;
  }
  .navbar-menu {
    display: none;
  }
  .navbar-menu.is-active {
    display: block;
  }
  .navbar-end {
    display: block;
  }
}

/* White text cannot brighten, so hover takes the interactive teal, as does the
   current page, which spa-navigation.js marks with is-active - the app's only
   "you are here". Bulma's selected state cannot do it: it changes the item's hue
   variables, but the text colour is composed on .navbar and inherited finished. */
.navbar-item:hover,
a.navbar-item.is-active {
  color: var(--bulma-link);
}

/* Bulma hides the divider below its desktop breakpoint; the burger menu it would
   separate now exists at all widths, so the divider does too. */
.navbar-menu .navbar-divider {
  display: block;
}

/* In the burger panel Bulma lays each item out as a block, so its .icon and label
   share a text line where vertical-align leaves the icon slightly high. Flex-align
   the item's contents to centre the icon on its label, as the desktop bar does. */
.navbar-menu .navbar-item {
  display: flex;
  align-items: center;
}

/* The burger panel's chrome. Bulma scopes the background, shadow and padding to
   its touch breakpoint, so forcing burger mode on desktop would leave the panel
   see-through; re-assert them here at all widths, since the panel is a burger
   panel everywhere now. The panel hangs below the bar, so only its bottom corners
   round, and overflow clips the last item's square corners off the rounded panel. */
.navbar-menu {
  background-color: var(--bulma-navbar-background-color);
  box-shadow: 0 0.5em 1em
    hsla(var(--bulma-scheme-h), var(--bulma-scheme-s), var(--bulma-scheme-invert-l), 0.1);
  padding: 0.5rem 0;
  border-end-start-radius: var(--bulma-radius);
  border-end-end-radius: var(--bulma-radius);
  overflow: hidden;
}

/* The person item rides to the right of the bar, just left of the hamburger: its
   auto-margin takes the free space and the burger tucks in beside it. A long name
   is truncated so it cannot push the burger off the bar. */
#current-user {
  margin-inline-start: auto;
}
#current-user ~ .navbar-burger {
  margin-inline-start: 0;
}
#current-user .is-truncated {
  max-width: 8rem;
}
/* Avatar slot: today just the user-circle glyph, a cropped photo in a later PR.
   It is its own flex wrapper rather than a Bulma .icon so the circle can fill the
   slot edge to edge instead of sitting inside a padded box. The glyph is sized to
   the full .icon box (1.5rem, not the 1.25rem tier) so its thin ring still reads
   as large as the neighbouring bar icons, and it hugs its label the way they do. */
.singer-avatar {
  display: inline-flex;
  margin-right: 0.125rem;
}
.singer-avatar .ph {
  font-size: 1.5rem;
}

/* The box and the first navbar icon share one left inset, so the bar icons line
   up with the body text beneath them. A single gutter drives both: the box's
   padding and the first brand item's left padding. A phone tightens the gutter
   for more room; the icons follow because they read the same variable. */
.container {
  --app-gutter: 1rem;
}
@media screen and (max-width: 768px) {
  .container {
    --app-gutter: 0.75rem;
  }
}
.box {
  --bulma-box-padding: var(--app-gutter);
}
/* The first item now sits flush in the navbar's rounded corner, so its active
   and hover background (Bulma paints it opaque) would square that corner off.
   Rounding the item's leading edge lets the fill follow the navbar instead. */
.navbar-brand .navbar-item:first-child {
  padding-left: var(--app-gutter);
  border-start-start-radius: var(--bulma-radius);
  border-end-start-radius: var(--bulma-radius);
}

/* Bar icons hug their glyph (Bulma gives .icon a fixed 1.5rem box) so the first
   one's glyph meets the gutter and each label sits close to its icon, not across
   the leftover box width. The glyph's own left margin goes too. The singer pill
   keeps the centred box: it pairs its mic with a name, and shrinking the box
   would pull the mic left. */
.navbar-brand .navbar-item:not(#current-user) > .icon {
  width: auto;
}
.navbar-brand .navbar-item:not(#current-user) > .icon::before {
  margin-left: 0;
}

.control-box a {
  color: var(--bulma-text);
}

.control-box a:hover {
  color: var(--bulma-text-strong);
}

/* Dimmed and non-interactive while the controls wait out the autoplay gap. */
.control-box.is-inert {
  opacity: 0.5;
  pointer-events: none;
}

.pagination {
  margin-bottom: 7px;
}
.pagination-page-info {
  padding: 0;
  width: 100%;
  margin: 0.5em;
  margin-left: 0;
  font-size: 12px;
}
.pagination-page-info b {
  color: var(--bulma-white);
}

input {
  /* prevents mobile zooming */
  font-size: 16px !important;
}

/* Section headings: Bulma's document scale, stepped down from the page title
   and bold (Lato has no 800 face). */
.box :is(h2, h3, h4, h5, h6) {
  color: var(--bulma-text-strong);
  font-weight: var(--bulma-weight-bold);
  line-height: 1.125;
}
.box h2 {
  font-size: 1.5em;
  margin-bottom: 0.5714em;
}
.box h2:not(:first-child) {
  margin-top: 1.1428em;
}
.box h3 {
  font-size: 1.25em;
  margin-bottom: 0.6666em;
}
.box h3:not(:first-child) {
  margin-top: 1.3333em;
}
.box h4 {
  font-size: 1.125em;
  margin-bottom: 0.8em;
}
.box h5,
.box h6 {
  font-size: 1em;
  margin-bottom: 0.8888em;
}

table {
  table-layout: fixed;
  width: 100%;
}

/* A fixed layout holds each column to its share, so a word longer than the
   column breaks rather than running into the next one. */
td {
  overflow-wrap: break-word;
}

.break-word {
  overflow-wrap: break-word; /* Renamed property in CSS3 draft spec */
  width: 100%;
}

/* Bulma has no truncation helper. */
.is-truncated {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Admin-only field naming who is about to sing. */
.singer-field {
  max-width: 320px;
  margin-bottom: 10px;
}

@media screen and (max-width: 600px) {
  /* Every data table tightens its side padding for column room on a phone; the
     dense report tables also shrink their type. Each owns its column widths. */
  .table {
    --bulma-table-cell-padding: 0.5em 4px;
  }
  .history-table,
  .rankings-table {
    font-size: 0.9rem;
  }
  /* Headers set smaller than the rows under them. They are single words that
     cannot wrap, so on a phone they, not the data, are what decides how narrow
     a column may be -- and they are read once rather than scanned down the
     page, which makes them the right thing to shrink. */
  .history-table th {
    font-size: 0.75rem;
  }
}

/* The cog opening a row's actions. The cog is an .icon.is-medium like any other
   icon, so only the anchor's pointer cursor is set here. */
.row-options-btn {
  cursor: pointer;
}

/* The cog is deliberately dim until pointed at, so a column of them does not
   compete with the rows. !important because it carries has-text-grey-light,
   and Bulma's colour helpers are themselves !important. */
.row-options-btn:hover {
  color: var(--bulma-text-strong) !important;
}

/* The add-to-queue plus brightens as a text link does, by Bulma's hover step on its
   own colour. !important for the same reason as the cog. */
.add-song-link.has-text-success:hover {
  color: hsl(
    var(--bulma-success-h),
    var(--bulma-success-s),
    calc(var(--bulma-success-l) + var(--bulma-hover-color-l-delta))
  ) !important;
}

/* Drag and drop highlight */
.drag-chosen {
  background-color: var(--bulma-info) !important;
  opacity: 0.9;
}

/* Brief highlight after move action */
.row-moved {
  animation: highlight-fade 1.5s ease-out;
}

@keyframes highlight-fade {
  0% { background-color: var(--bulma-info); }
  100% { background-color: transparent; }
}

/* Format badge on edit page */
.format-badge {
  height: 1.2em;
  vertical-align: middle;
  opacity: 0.6;
  flex-shrink: 0;
}

/* A div, not <progress>, so the fill can transition; the duration matches the poll. */
.dl-bar {
  /* A lighter grey on the card's has-background-dark, so the empty track shows
     before the fill covers it. */
  background-color: var(--bulma-grey-dark);
  border-radius: var(--bulma-radius-rounded);
  height: 8px;
  overflow: hidden;
  position: relative;
  width: 100%;
}

.dl-bar-fill {
  background-color: var(--bulma-link);
  border-radius: inherit;
  height: 100%;
  position: relative;
  transition: width 500ms linear;
  width: 0;
}

/* Whenever no percentage is arriving - the resolve, the seam between the two passes,
   the merge - a highlight travels instead of a number advancing. It rides the empty
   track before the first byte and the fill after it, hence the opposite contrast.
   transform keeps the animation off the Pi's CPU. */
.dl-bar.is-waiting::after,
.dl-bar-fill.is-waiting::after {
  content: "";
  position: absolute;
  inset: 0;
  animation: dl-sheen 1.6s linear infinite;
}

/* Grey, not the accent: a coloured sweep on the empty track would read as real fill. */
.dl-bar.is-waiting::after {
  background: linear-gradient(90deg, transparent, var(--bulma-text-weak), transparent);
  opacity: 0.35;
}

.dl-bar-fill.is-waiting::after {
  background: linear-gradient(90deg, transparent, var(--bulma-white), transparent);
  opacity: 0.28;
}

@keyframes dl-sheen {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
  input.switch[type="checkbox"],
  input.switch[type="checkbox"]::before {
    transition: none;
  }

  .dl-bar-fill {
    transition: none;
  }

  .dl-bar.is-waiting::after,
  .dl-bar-fill.is-waiting::after {
    animation: none;
    opacity: 0.2;
  }
}

/* Wraps, unlike the .level.is-mobile it replaces: the phase and the three figures
   need more room than a phone gives them. The auto margin is on the span wrapping
   the phase so the figures stay grouped at the right, and so the rate stays
   right-aligned when it drops to a second line. */
.dl-meta {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 2px 16px;
  justify-content: flex-end;
  margin-top: 10px;
  min-height: 17px;
}

/* Tabular figures and a floor on the width: without both, the percentage and the
   speed change width on every 500 ms poll and the whole row twitches. */
.dl-percent {
  color: var(--bulma-text-strong);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  min-width: 3.2em;
  text-align: right;
}

.dl-meta-rate {
  color: var(--bulma-grey-light);
  display: flex;
  font-variant-numeric: tabular-nums;
  gap: 12px;
}
