/* Copyright (c) 2025 In Flux Studio Co. All rights reserved. See LICENSE. */
/* =============================================================================
   Utility Classes
   Single-purpose, reusable utility classes for common styling patterns.
   ============================================================================= */

/* =============================================================================
   Layout Utilities
   ============================================================================= */

.bottom {
  align-items: flex-end;
  display: flex;
}

.center {
  text-align: center;
  align-items: center;
  justify-content: center;
}

.text-right {
  text-align: right;
}

.thumbnail-preview {
  max-width: 24rem;
  border-radius: 0.25rem;
}

.column {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  min-height: 0; /* Allow shrinking below content size in flex contexts */
  height: auto;
  gap: 0.5rem;
}

.row {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  min-height: 0; /* Allow shrinking below content size in flex contexts */
  gap: 0.5rem;
}

/* Justification: <br> is treated as a flex item inside .row and does not
   force a line break. flex-basis: 100% is the standard flex-line-break
   trick, and no existing utility expresses it (.full-width sets width,
   which doesn't trigger wrapping). Apply to a flex item to span the full
   row so subsequent siblings wrap to the next line. */
.row-break {
  flex-basis: 100%;
}

/* Two-column label/input grid. Labels auto-size to the widest, inputs fill the rest.
   Non-label direct children (headings, paragraphs, dividers) span both columns. */
.field-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.5rem;
}

.field-grid > div,
.field-grid > h1,
.field-grid > h2,
.field-grid > h3,
.field-grid > h4,
.field-grid > h5,
.field-grid > h6,
.field-grid > p,
.field-grid > hr {
  grid-column: 1 / -1;
}

/* Sticky header bar — other sticky elements must offset by --nav-height to stack below */
.menu-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  background-color: var(--ground);
}

.full-width {
  width: 100%;
}

.left {
  display: flex;
  align-items: flex-start;
}

.right {
  margin-left: auto;
}

/* =============================================================================
   Flexbox Utilities
   ============================================================================= */

.align-items-center {
  align-items: center;
}

/* Justification: explicit flex justification on a child element when the
   parent .row's default isn't suitable. Cannot use .center (which adds
   text-align + align-items) when only horizontal flex alignment is wanted
   without affecting text or cross-axis alignment. */
.justify-content-center {
  justify-content: center;
}

/* Justification: explicit right alignment for flex children that need to
   sit at the end of a row. Distinct from .ml-auto (which pushes a single
   item right via margin) — this applies main-axis end alignment to the
   whole flex container. */
.justify-content-flex-end {
  justify-content: flex-end;
}

/* =============================================================================
   Spacing Utilities
   ============================================================================= */

.ml-auto {
  margin-left: auto;
}

/* Justification: pushes a flex child upward by absorbing remaining space
   below it (`margin-bottom: auto` is the flex-item complement to
   `margin-left: auto`). Used in pages/timeline.html on a row that needs
   to stick to the top of its column container. No existing utility
   covers cross-axis margin-auto. */
.margin-bottom-auto {
  margin-bottom: auto;
}

/* Card: self-contained block with background, padding, fit-content width.
   Use on <article>, <section>, or <div> for list-of-things UIs.
   flex: none keeps it from growing when placed inside a .row flex container. */
.card {
  background-color: var(--section);
  border-radius: 0.5rem;
  width: fit-content;
  flex: none;
  padding: 0.5rem;
}

/* =============================================================================
   Text Utilities
   ============================================================================= */

.text-bold {
  font-weight: bold;
}

.subtle {
  color: var(--figure-subtle);
}

.help-text {
  color: var(--figure-subtle);
  max-width: none;
  font-weight: normal;
}

/* Justification: replaces .code-area in common.css. Multiple callsites repeat
   font-family: monospace inline; this is the only piece of .code-area worth
   keeping (the dark theme/border/padding/resize were too opinionated for a
   shared class). Promotes the one reusable property to a utility. */
.monospace {
  font-family: monospace;
}

/* Justification: spec-safe alternative to <textarea wrap="off">. The `wrap`
   attribute's "off" value is widely supported but not in the HTML spec
   (only "soft" and "hard" are defined). Pairing this with .scroll on a
   textarea produces the same horizontal-scroll-on-long-lines behavior via
   pure CSS. */
.preserve-whitespace {
  white-space: pre;
}

/* =============================================================================
   Accessibility
   ============================================================================= */

/* Screen reader only - visually hidden but accessible */
.sr-only {
  clip: rect(0, 0, 0, 0);
  border: 0;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
}

/* =============================================================================
   Container Utilities
   ============================================================================= */

/* Scrollable container - simple approach */
.scrollable {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.shadow {
  box-shadow: 0rem 0.5rem 2rem var(--shadow-color);
}

/* =============================================================================
   Button Utilities
   ============================================================================= */

/* Justification: square icon-only button used by row-action toolbars across
   stacks/synced-stacks/projects/variables list-view configs and a few
   modals. Sizing matches the 44px tap-target threshold from the global
   button rule in measured-light.css; uses 2.75rem (= 44px at 16px base)
   to comply with the rem-first CSS rule. Cannot compose from utilities
   because `aspect-ratio: 1` + grid place-items is the load-bearing pair. */
.square-button {
  aspect-ratio: 1;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  display: grid;
  place-items: center;
}

/* Full-width with left-aligned text - for list items */
.block-left {
  width: 100%;
  text-align: left;
}

/* =============================================================================
   Color Utilities
   ============================================================================= */

.primary {
  color: var(--figure);
  background-color: var(--c2);
}

.secondary {
  color: var(--figure);
  background-color: var(--c3);
}

.tertiary {
  color: var(--figure);
  background-color: var(--c4);
}

.override {
  color: var(--figure);
  background-color: var(--ground-warning);
}

.error {
  color: var(--figure-error);
}

.danger-button {
  background: var(--ground-warning);
  color: var(--figure);
}

.danger-button:hover {
  background: var(--danger-dark);
  color: var(--figure);
}

/* =============================================================================
   Flex Item Utilities
   ============================================================================= */

/* Prevent flex item from shrinking below content size */
.no-shrink {
  flex-shrink: 0;
}

/* Prevent flex row from wrapping */
.nowrap {
  flex-wrap: nowrap;
}

/* Truncate text with ellipsis */
.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Allow flex item to grow to fill available space */
.flex-grow {
  flex: 1;
  min-width: 0; /* Allow text truncation in flex items */
}

/* Justification: baseline "be scrollable" primitive. Browsers do not
   auto-scroll any element; you must opt in. .scroll-y already exists for the
   "never horizontal" specialization, but the both-axes case (long lines that
   should hscroll, e.g. log/code previews) had no canonical class — callers
   were either inlining `style="overflow:auto"` or misusing .scroll-y and
   silently clipping x-overflow. */
.scroll {
  overflow: auto;
}

/* Vertical-only scrollable list */
.scroll-y {
  overflow-x: hidden;
  overflow-y: auto;
}

/* Justification: bounded, vertically-scrollable region for embedded long-form
   content the user reviews in place (e.g. inline legal text on the accept
   page) — the existing .scroll/.scroll-y set overflow but cap nothing. Make the
   element focusable with an accessible name (tabindex="0" + aria-label, or
   role="region" on a non-section element) so keyboard users can scroll it. */
.scroll-box {
  max-height: 13rem;
  overflow-y: auto;
  padding: 0.5rem;
}

/* Collapse a disclosure body when its <details> is closed. The native
   closed-content hiding is unreliable under this app's element resets, so we
   hide it explicitly — mirroring the nav-menu panel pattern in main-nav.css. */
details:not([open]) > .scroll-box {
  display: none;
}

/* Disclosure triggers are clickable — match the pointer cursor used on the
   adjacent consent checkboxes. */
details > summary {
  cursor: pointer;
}

/* =============================================================================
   Visibility Utilities
   ============================================================================= */

/* Hidden until ancestor has data-edit-state="dirty" or "saving" */
.hidden-until-dirty {
  display: none;
}

[data-edit-state='dirty'] .hidden-until-dirty,
[data-edit-state='saving'] .hidden-until-dirty {
  display: inline;
}

/* =============================================================================
   Animation Utilities
   ============================================================================= */

/* Pulsing background between secondary and ground */
@keyframes throb {
  0%,
  100% {
    background-color: var(--c4);
  }
  50% {
    background-color: var(--ground);
  }
}

@keyframes throb-hover {
  0%,
  100% {
    background-color: var(--c4);
  }
  50% {
    background-color: var(--figure);
  }
}

.throb {
  animation: throb 1597ms ease-in-out infinite;
}

.throb:hover {
  animation-name: throb-hover;
}
