/* ==========================================================================
   Design System Components — Compass (Yozu)
   Ported from Clarity (yozu-uk/google-ads-platform). Token layer lives in
   app/assets/tailwind/tokens.css, driven by the [data-theme] attribute on
   <html>. This file defines component classes that consume those tokens
   via var(--token-name). Only the sections consumed by the ported
   component set are carried over; chart / sidebar / editor chrome stays
   in Clarity.

   Do NOT add new :root / :host token declarations here — extend tokens.css.
   ========================================================================== */


/* --------------------------------------------------------------------------
   Data Tables (components/_table + _table_row + _data_table)
   -------------------------------------------------------------------------- */
.data-table-wrap {
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: 12px;
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
/* Horizontal-scroll container INSIDE the chrome. Scrolling lives on its
   own element so toolbar rows (quick filter / title) can sit inside the
   card without scrolling with the table. */
.data-table-scroll {
  overflow-x: auto;
}
.data-table-wrap__header {
  padding: 1rem 1.5rem;
  border-bottom: 1px solid var(--border-subtle);
}
.data-table-wrap__title {
  font-family: var(--font-display);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-primary);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
}
.data-table thead th {
  padding: 0.75rem 1.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
  text-align: left;
  border-bottom: 1px solid var(--border-subtle);
  position: sticky;
  top: 0;
  background: var(--surface-table-header);
  z-index: 1;
}
.data-table thead th.text-right {
  text-align: right;
}
.data-table tbody tr {
  border-bottom: 1px solid var(--border-subtle);
  transition: background var(--transition-fast);
}
.data-table tbody tr:last-child {
  border-bottom: none;
}
.data-table tbody tr:hover {
  background: var(--surface-hover);
}
.data-table tbody td {
  padding: 0.75rem 1.5rem;
  font-size: 0.875rem;
  color: var(--text-secondary);
}
.data-table tbody td.text-right {
  text-align: right;
}
.data-table tbody td .font-mono,
.data-table tbody td.font-mono {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--text-primary);
}
.data-table .link {
  color: var(--text-primary);
  font-weight: 500;
  text-decoration: none;
  transition: color var(--transition-fast);
}
.data-table .link:hover {
  color: var(--accent-dim);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* --------------------------------------------------------------------------
   Responsive table — card mode below md, sticky first column above md.
   -------------------------------------------------------------------------- */
@media (min-width: 768px) {
  /* Sticky first column on horizontal scroll for tabular layout. */
  .data-table tbody td:first-child,
  .data-table thead th:first-child {
    position: sticky;
    left: 0;
    background: var(--surface-card);
    z-index: 2;
  }
  .data-table thead th:first-child {
    background: var(--surface-table-header);
    z-index: 3;
  }
  /* Scroll-shadow on the right edge when the table overflows its
     wrapper. Without this, narrow viewports clip the last column with
     no visual cue that more is scrollable. The gradient is
     local-attached on the surface and scroll-attached on the shadow —
     at the right edge the shadow rolls off-screen, indicating "no more
     content this way". */
  .data-table-scroll {
    background:
      linear-gradient(to right, var(--surface-card) 30%, transparent),
      linear-gradient(to left, var(--surface-card) 30%, transparent),
      linear-gradient(to right, rgba(0, 0, 0, 0.08), transparent 30%),
      linear-gradient(to left, rgba(0, 0, 0, 0.08), transparent 30%);
    background-attachment: local, local, scroll, scroll;
    background-position: 0 0, 100% 0, 0 0, 100% 0;
    background-repeat: no-repeat;
    background-size: 24px 100%, 24px 100%, 12px 100%, 12px 100%;
  }
}

@media (max-width: 767px) {
  /* Stack each row as a card; visually hide the thead. */
  .data-table-scroll {
    overflow-x: visible;
  }
  .data-table,
  .data-table thead,
  .data-table tbody,
  .data-table tr,
  .data-table th,
  .data-table td {
    display: block;
    width: 100%;
  }
  .data-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
  }
  .data-table tbody tr {
    border: 1px solid var(--border-subtle);
    border-radius: 8px;
    margin: 0.5rem 0;
    padding: 0.5rem 0.75rem;
    background: var(--surface-card);
  }
  .data-table tbody td {
    border: none;
    padding: 0.375rem 0;
    text-align: left !important;
  }
  /* `data-label="Column"` rendered as a small caption before the cell
     content so users can identify the field without the header row. */
  .data-table tbody td[data-label]::before {
    content: attr(data-label) ":\00a0";
    display: inline;
    color: var(--text-secondary);
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-right: 0.25rem;
  }
}


/* --------------------------------------------------------------------------
   Data table shell — one unified card: toolbar + table + pagination.
   The shell carries the card chrome; the inner .data-table-wrap renders
   flush (no border/radius/shadow) so the pieces read as a single surface.
   -------------------------------------------------------------------------- */
.data-table-shell {
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: 12px;
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
.data-table-wrap--flush {
  border: none;
  border-radius: 0;
  box-shadow: none;
}

/* Server-sorted column headers: the whole header cell is the link.
   The th drops its own padding so the link's hit area fills the cell. */
.data-table thead th.data-table__th--sort {
  padding: 0;
}
.data-table__sort-link {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  width: 100%;
  padding: 0.75rem 1.5rem;
  color: inherit;
  text-decoration: none;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.data-table__sort-link--numeric {
  justify-content: flex-end;
}
.data-table__sort-link:hover,
.data-table__sort-link:focus-visible {
  background: var(--surface-hover);
  color: var(--text-primary);
}
/* Sortable-but-inactive affordance: chevron-up-down fades in on hover/focus. */
.data-table__sort-hint {
  opacity: 0;
  transition: opacity var(--transition-fast);
}
.data-table__sort-link:hover .data-table__sort-hint,
.data-table__sort-link:focus-visible .data-table__sort-hint {
  opacity: 1;
}

/* Toolbar filter controls: native selects styled as compact dropdown
   buttons with an embedded funnel icon (left) and the form-select
   chevron (right). Scoped under .data-table-filter so this beats the
   .form-select base rule regardless of source order — .form-select's
   `padding` shorthand otherwise clobbers the left inset and the text
   renders underneath the funnel icon. padding-right must clear the
   chevron background-image (12px at right 0.625rem). */
.data-table-filter .data-table-filter__select {
  height: 36px;
  max-width: 13rem;
  padding: 0 2rem 0 1.9rem;
  font-size: 0.8125rem;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}
.data-table-filter--active .data-table-filter__icon {
  color: var(--accent);
}
.data-table-filter--active .data-table-filter__select {
  border-color: var(--accent);
  color: var(--text-primary);
}


/* --------------------------------------------------------------------------
   Form Controls (components/_form_field)
   -------------------------------------------------------------------------- */
.form-label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-secondary);
  margin-bottom: 0.375rem;
}

.form-input {
  width: 100%;
  height: 40px;
  padding: 0 0.75rem;
  font-size: 0.875rem;
  color: var(--text-primary);
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: 8px;
  transition: border-color var(--transition-fast),
              box-shadow var(--transition-fast);
  outline: none;
}
.form-input::placeholder {
  color: var(--text-muted);
}
.form-input:hover {
  border-color: var(--border-strong);
}
.form-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
}
/* The accent border + soft glow above IS the focus indicator; the
   global *:focus-visible outline on top reads as a double box. */
.form-input:focus-visible,
.form-select:focus-visible {
  outline: none;
}

/* Same rationale for the chat composer: its CONTAINER shows focus via
   focus-within:border-accent, so the inner textarea must not draw the
   global ring (autofocus counts as :focus-visible in Chromium). This
   lives here, unlayered, because Tailwind's focus:outline-none sits in
   @layer utilities and always loses to the unlayered global above. */
.composer-input:focus-visible {
  outline: none;
}
.form-input--mono {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
}

.form-select {
  height: 36px;
  padding: 0 2rem 0 0.75rem;
  font-size: 0.8125rem;
  color: var(--text-primary);
  background-color: var(--surface-card);
  /* SVG arrow uses currentColor so it tracks the text colour in light
     and dark themes. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5l3 3 3-3' stroke='currentColor' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.625rem center;
  border: 1px solid var(--border-default);
  border-radius: 8px;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  transition: border-color var(--transition-fast),
              box-shadow var(--transition-fast);
  outline: none;
}
.form-select:hover {
  border-color: var(--border-strong);
}
.form-select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
}

.form-hint {
  font-size: 0.75rem;
  color: var(--text-tertiary);
  margin-top: 0.25rem;
}


/* --------------------------------------------------------------------------
   Empty State (components/_empty_state)
   -------------------------------------------------------------------------- */
.empty-state {
  color: var(--text-tertiary);
}


/* --------------------------------------------------------------------------
   Shared keyframes
   -------------------------------------------------------------------------- */

/* Toast entry (components/_toast: motion-safe:animate-[flash-enter…]). */
@keyframes flash-enter {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Pulsing status dot (components/_status_dot pulsing: true). */
@keyframes pulse-glow {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}


/* --------------------------------------------------------------------------
   Global / Misc
   -------------------------------------------------------------------------- */

/* Scrollbar — theme-aware. Mixing from --text-primary keeps the thumb
   visible on both themes (a black rgba vanishes on dark pages). */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--text-primary) 16%, transparent);
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--text-primary) 28%, transparent);
}

/* Focus visible global — keyboard-only focus indicator using the
   tokenised --ring-focus colour. 2px outline + offset reads on every
   surface; border-radius: inherit means rounded controls keep their
   shape. focus-visible (not :focus) ensures mouse users don't see
   the ring on click. */
*:focus-visible {
  outline: 2px solid var(--ring-focus);
  outline-offset: 2px;
  border-radius: inherit;
}

/* Selection colour — token-driven. Tinting the background only and
   inheriting the text colour works on every surface in both themes. */
::selection {
  background: color-mix(in srgb, var(--accent) 30%, transparent);
}

/* Submit button loading state. When the submit_button Stimulus
   controller flips data-loading=true on a button, dim the label and
   tee up an inline spinner glyph via a pseudo-element so authors don't
   have to add markup per call site. */
button[data-loading="true"],
[data-loading="true"][role="button"] {
  pointer-events: none;
  opacity: 0.7;
  position: relative;
}
@media (prefers-reduced-motion: no-preference) {
  button[data-loading="true"]::after,
  [data-loading="true"][role="button"]::after {
    content: "";
    display: inline-block;
    width: 0.875em;
    height: 0.875em;
    margin-left: 0.5em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 600ms linear infinite;
    vertical-align: -2px;
  }
  @keyframes spin {
    to { transform: rotate(360deg); }
  }
}

/* Turbo view transitions for primary nav. Wrapped in motion-safe so
   reduced-motion users get instant page swaps. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 200ms;
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  }
}

/* Reduced-motion safety net. Existing rules (skeleton shimmer,
   view-transitions, submit-button spinner) are already wrapped in
   motion-safe queries. This blanket rule neutralises any remaining
   unguarded transitions / animations so a reduced-motion user never
   sees easing or keyframe motion — state changes happen instantly
   without breaking functionality. WCAG 2.1 SC 2.3.3. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  /* Essential progress indicators are the WCAG 2.3.3 exception: a
     frozen spinner reads as "nothing is happening". Slow the spin
     right down instead of freezing it. `spin` keyframes come from
     the Tailwind build. */
  .progress-spinner {
    animation: spin 2s linear infinite !important;
  }

  /* The chat's thinking indicators are the same exception (2026-07-18
     UAT, Windows Chrome: its "animation effects" toggle reports
     reduced-motion, and the global kill above froze these into a
     hung-looking app). Keyframes live in the Tailwind build; the
     !important is required to out-rank the global kill. */
  .typing-dots > span {
    animation: typing-dot 1.6s ease-in-out infinite !important;
  }
  .compass-thinking {
    animation: needle-settle 3s ease-in-out infinite !important;
  }
}
