/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 */

html, body {
  margin: 0;
  padding: 0;
  overflow-x: hidden; /* optional, prevents horizontal scroll */
}

/* ── The dock ───────────────────────────────────────────────────────────
 * Three names, one mechanism: `shared/_dock` is the fixed wrapper, #bottom-bar
 * is the stack of full-width rows inside it (today just the music player), and
 * --dock-h is how tall that stack currently is.
 *
 * Declare each row's height *here, once*; anything else anchored to the bottom
 * derives from --dock-h instead of restating the number. See docs/patterns/ui.md.
 *
 * Defaults, overrides and the total all live on `body` deliberately. var()
 * substitutes per element, so a default on :root with the override here would
 * resolve --dock-h against :root's zero and inherit down as a constant 0 —
 * silently reserving nothing. Same element, no trap.
 *
 * The padding clears the stack for in-flow content, so footer actions (Delete /
 * Update / Back) aren't covered. On `body` rather than `main` for a second
 * reason: the admin layout's main carries a `py-6` utility, and a class
 * selector outranks a bare element one — the padding would silently lose. */
body {
  --player-h: 0rem;
  --dock-h: var(--player-h);
  padding-bottom: var(--dock-h);
}

body:has(#music-player-bar:not(.hidden)) { --player-h: 6rem; }

/* Safe-area (notch / gesture bar) support — viewport-fit=cover is set in the
 * head, so whatever touches the bottom edge must clear the home indicator
 * itself. env() resolves to 0 on devices without an inset, leaving desktop
 * untouched. `body` prefix outranks the bottom-* utility so cascade order
 * between stylesheets can't flip the result.
 *
 * The inset rides the bottom-most *visible* row, not the #bottom-bar wrapper:
 * the wrapper is 0-tall while the player is hidden, and padding on it would
 * leave an invisible full-width strip over the gesture area swallowing taps on
 * every page. A row added below the player takes the inset over and this drops
 * back to a bare 0.5rem — only one row may carry it, or the gesture bar gets
 * counted twice. (Overlays that escape the bar — the queue drawer and its
 * backdrop — are `fixed` in their own right and handle their own inset below.)
 *
 * The ID selector outranks the bar's own `py-2`, so the 0.5rem base must be
 * restated here — a bare env() would zero the bottom padding wherever the
 * inset is 0 (i.e. every desktop). */
#music-player-bar {
  padding-bottom: calc(0.5rem + env(safe-area-inset-bottom));
}

[data-music-player-target="queue"] {
  padding-bottom: env(safe-area-inset-bottom);
}

/* Fixed elements ignore the body padding above, so they clear the stack
 * themselves. --dock-h collapses to 0 when the dock is empty, which leaves all
 * three at exactly their pre-dock resting position. */
body [data-bulk-select-target="actionBar"] {
  bottom: calc(1rem + var(--dock-h) + env(safe-area-inset-bottom));
}

/* The mobile "+" FAB: above the gesture inset, and above the dock. */
#new-fab {
  bottom: calc(1.875rem + var(--dock-h) + env(safe-area-inset-bottom));
}

/* Flash toasts, same deal — they sat at a bare bottom-4 and rendered over the
 * player whenever a track was loaded. */
body #flash-dispenser {
  bottom: calc(1rem + var(--dock-h) + env(safe-area-inset-bottom));
}

/* 16px floor on every text-entry control — anything smaller makes iOS
 * Safari auto-zoom the page on focus. The DaisyUI field classes set
 * font-size: .875rem at class specificity, which outranks the bare
 * element selectors — the floor must restate them as compound selectors
 * (input.input beats .input) or it silently loses on every
 * DaisyUI-classed field. trix-editor (contenteditable, not a textarea)
 * and the tagger's dynamically created type-less input need naming
 * explicitly. */
input[type="text"],
input[type="search"],
input[type="email"],
input[type="password"],
input[type="url"],
input[type="number"],
textarea,
input.input,
textarea.textarea,
select.select,
trix-editor,
trix-editor.textarea,
.tagger input {
  font-size: 16px;
}

/* Trix toolbar: wrap instead of clipping on narrow screens; real tap
 * targets where the pointer is a finger. */
trix-toolbar .trix-button-row {
  flex-wrap: wrap;
}

@media (pointer: coarse) {
  trix-toolbar .trix-button {
    min-height: 2.75rem;
    min-width: 2.5rem;
  }
}

/* Turbo drives View Transitions (meta[name="view-transition"] in the
 * head). Keep the cross-fade fast so navigation feels snappy rather than
 * animated, and drop it entirely for reduced-motion users. */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 150ms;
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
  }
}

/* Visible keyboard focus everywhere, tokened to the theme so it holds
 * in both solarized variants. :focus-visible only — mouse clicks don't
 * get the ring. */
:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Reduced motion: collapse every animation/transition to a single
 * frame. The View Transition rule above handles navigation; this
 * catches the rest (spinner, skeleton pulse, flash slide, queue-drawer
 * slide) without needing per-component opt-outs. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* Global-search toggle: ring the search input while it's searching the whole
 * library. `outline` (not box-shadow) so it doesn't fight the input's shadow
 * utility. Paired with the highlighted pill — see filter_controller#toggleScope. */
.is-global-search {
  outline: 2px solid var(--color-warning, #b58900);
  outline-offset: 2px;
  border-radius: 9999px;
}

.github-signin-container {
  background-color: #000;
  transition: background-color 0.3s ease;
  border-radius: 6px;
  border: none;
}

.github-signin-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #000;
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  padding: 10px 20px;
  border: 2px solid #1b1f23;
  cursor: pointer;
  text-decoration: none;
  transition: background-color 0.3s ease;
  width: 100%;
}

.github-signin-btn .github-icon {
  width: 64px;
  margin-right: 8px;
}

.github-signin-btn span {
  font-family: Arial, sans-serif;
  font-size: 16px;
}

.animated-border {
    stroke: var(--color-base-content);
    stroke-dasharray: 711.5;
    stroke-dashoffset: -711.5;
    stroke-width: 3;
    transition: stroke-dashoffset 1s ease-in-out;

    transform: rotate(-270deg);
    transform-origin: 50% 50%;
    stroke-linecap: round;
}

.yin-yang-container {
  width: 100%;
  height: auto
}

.yin-yang-container:hover .animated-border {
    stroke-dashoffset: 0;
}

/* #theme-toggler */
#theme-toggler::before {
  background-color: var(--color-secondary);
}
/* #theme-toggler */

/* Comment composer
 *
 * Trix ships a full formatting toolbar. On a comment box that is most of the
 * chrome on screen before anyone has typed a word, so it stays collapsed until
 * the editor is reached for. grid-template-rows (not height/max-height) so the
 * row animates to the toolbar's real height rather than a guessed one.
 *
 * Two tracks, because trix-toolbar has two children: the buttons and
 * .trix-dialogs, which is where the link popover opens ~75px below them. The
 * clip that makes the collapse work has to sit on the button row alone — on
 * trix-toolbar it also swallows that popover, and the link button reads as
 * dead because pressing it shows nothing.
 */
.comment-composer trix-toolbar {
  display: grid;
  grid-template-rows: 0fr auto;
  opacity: 0;
  transition: grid-template-rows 0.2s ease, opacity 0.2s ease;
}

.comment-composer:focus-within trix-toolbar {
  grid-template-rows: 1fr auto;
  opacity: 1;
}

.comment-composer trix-toolbar .trix-button-row {
  min-height: 0;
  overflow: hidden;
}

.comment-composer trix-editor {
  border: 0;
  min-height: 3.5rem;
  padding: 0.5rem 0.75rem;
}

/* Ring the composer, not the contenteditable inside it. trix-editor is a
 * text-entry element, so it matches :focus-visible on a plain mouse click and
 * takes the global outline — which, offset 2px into a box whose only interior
 * gap is the toolbar seam, drew as a stray blue rule across the editor's top
 * edge and nowhere else. :focus-within matches what opens the toolbar, so the
 * box lights up exactly while it is open. */
.comment-composer trix-editor:focus-visible {
  outline: none;
}

.comment-composer:focus-within {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* The toolbar stays in the tab order on purpose: :focus-within opens the
 * composer, so tabbing into a collapsed button reveals it rather than moving
 * focus somewhere invisible.
 */
@media (prefers-reduced-motion: reduce) {
  .comment-composer trix-toolbar {
    transition: none;
  }
}

/* ── The page-flow diagram ──────────────────────────────────────────────
 * /admin/development/flow, drawn as SVG by flow_map_controller. Everything
 * here is expressed in DaisyUI's theme variables rather than literal colours,
 * so the map follows the solarized-light/solarized-dark toggle like the rest
 * of the app — an SVG with baked-in hex values is the classic way to end up
 * with a diagram that is unreadable in one theme and nobody notices.
 */
.flow-canvas {
  touch-action: none;
  cursor: grab;
  user-select: none;
}

.flow-canvas:active {
  cursor: grabbing;
}

.flow-node {
  cursor: pointer;
}

.flow-node__box {
  fill: var(--color-base-100);
  stroke: var(--color-base-300);
  stroke-width: 1;
}

.flow-node__accent {
  fill: var(--color-base-300);
}

.flow-node__title {
  font: 600 11px ui-monospace, SFMono-Regular, Menlo, monospace;
  fill: var(--color-base-content);
}

.flow-node__path {
  font: 400 10px ui-monospace, SFMono-Regular, Menlo, monospace;
  fill: var(--color-base-content);
  opacity: 0.55;
}

/* Section accents. The left rail is the only thing carrying section identity,
 * deliberately — colouring the whole box turns a 90-node map into a swatch
 * chart and makes the selection state impossible to see. */
.flow-node[data-section="admin"] .flow-node__accent { fill: var(--color-warning); }
.flow-node[data-section="public"] .flow-node__accent { fill: var(--color-success); }
.flow-node[data-section="auth"] .flow-node__accent { fill: var(--color-info); }
.flow-node[data-section="development"] .flow-node__accent { fill: var(--color-error); }
.flow-node[data-section="chat"] .flow-node__accent,
.flow-node[data-section="map"] .flow-node__accent,
.flow-node[data-section="billing"] .flow-node__accent { fill: var(--color-secondary); }

.flow-node.is-selected .flow-node__box {
  stroke: var(--color-primary);
  stroke-width: 2;
  fill: color-mix(in oklab, var(--color-primary) 14%, var(--color-base-100));
}

.flow-node.is-dimmed { opacity: 0.2; }
.flow-node.is-hidden,
.flow-edge.is-hidden { display: none; }

/* Hidden until a note exists for the node; flow_map_controller flips the class
 * from whatever is currently in the note list. */
.flow-node__badge { display: none; }
.flow-node.has-notes .flow-node__badge { display: block; }
.flow-node__badge circle { fill: var(--color-warning); }

.flow-node__badge-count {
  font: 600 10px ui-monospace, SFMono-Regular, Menlo, monospace;
  fill: var(--color-base-100);
  text-anchor: middle;
}

.flow-edge {
  fill: none;
  stroke: var(--color-base-content);
  opacity: 0.16;
}

.flow-edge.is-active {
  stroke: var(--color-primary);
  opacity: 0.75;
}

/* Zoomed out past the point where 10px type means anything. Dropping the text
 * is also what keeps a full-map zoom from painting several hundred glyph runs
 * on every pan frame. */
.flow-canvas.is-far .flow-node__title,
.flow-canvas.is-far .flow-node__path { display: none; }
