<script lang="ts">
  import { api } from '$lib/api';
  import { toast } from '$lib/toast';
  import AppSecondaryRailLayout from '$lib/components/navigation/AppSecondaryRailLayout.svelte';
  import ChangeHistoryModal from '$lib/components/editor/ChangeHistoryModal.svelte';
  import SortHeader from '$lib/table/SortHeader.svelte';
  import { TableController } from '$lib/table/table.svelte';
  import { formatNumber } from '$lib/format';
  import type { EditorIngredientRow } from '$lib/types';
  import { ChevronLeft, ChevronRight, FlaskConical, History, ListFilter, Plus, Save, Search, X } from 'lucide-svelte';
  import { fade } from 'svelte/transition';

  let { data } = $props();

  // The selectable rounding options (decimal places) offered per ingredient.
  const ROUNDING_OPTIONS = [0, 1, 2, 3];

  type EditableIngredient = EditorIngredientRow & {
    draft_name: string;
    draft_unit_of_measure: string;
    draft_kg_per_unit: number | string;
    draft_status: string;
    draft_rounding_decimals: number;
    draft_category: string;
  };

  function toEditable(row: EditorIngredientRow): EditableIngredient {
    return {
      ...row,
      draft_name: row.name,
      draft_unit_of_measure: row.unit_of_measure,
      draft_kg_per_unit: row.kg_per_unit,
      draft_status: row.status,
      draft_rounding_decimals: row.rounding_decimals,
      draft_category: row.category ?? ''
    };
  }

  let rows = $state<EditableIngredient[]>([]);
  let query = $state('');
  let statusFilter = $state<'active' | 'archived' | 'all'>('active');
  let savingKey = $state<string | null>(null);
  // The ingredient whose change history is open in the modal (null = closed).
  let historyIngredient = $state<EditableIngredient | null>(null);

  $effect(() => {
    if (rows.length === 0 && (data.ingredients as EditorIngredientRow[]).length > 0) {
      rows = (data.ingredients as EditorIngredientRow[]).map(toEditable);
    }
  });

  function isActive(status: string) {
    return status.toLowerCase() === 'active';
  }

  function rowDirty(row: EditableIngredient) {
    return (
      row.draft_name.trim() !== row.name ||
      row.draft_unit_of_measure.trim() !== row.unit_of_measure ||
      Number(row.draft_kg_per_unit) !== row.kg_per_unit ||
      row.draft_status !== row.status ||
      Number(row.draft_rounding_decimals) !== row.rounding_decimals ||
      row.draft_category.trim() !== (row.category ?? '')
    );
  }

  function applyUpdate(updated: EditorIngredientRow) {
    rows = rows.map((row) => (row.id === updated.id ? toEditable(updated) : row));
  }

  function resetRow(row: EditableIngredient) {
    rows = rows.map((candidate) => (candidate.id === row.id ? toEditable(candidate) : candidate));
  }

  async function saveRow(row: EditableIngredient) {
    if (!rowDirty(row)) return;
    if (!row.draft_name.trim()) {
      toast.error('Ingredient needs a name.');
      return;
    }
    if (!row.draft_unit_of_measure.trim()) {
      toast.error('Ingredient needs a unit of measure.');
      return;
    }
    if (!(Number(row.draft_kg_per_unit) > 0)) {
      toast.error('Kg per unit must be greater than zero.');
      return;
    }
    savingKey = `row:${row.id}`;
    try {
      applyUpdate(
        await api.updateEditorIngredient(row.id, {
          name: row.draft_name.trim(),
          unit_of_measure: row.draft_unit_of_measure.trim(),
          kg_per_unit: Number(row.draft_kg_per_unit),
          status: row.draft_status,
          rounding_decimals: Number(row.draft_rounding_decimals),
          category: row.draft_category.trim() || null
        })
      );
      toast.success('Ingredient saved');
    } catch (error) {
      toast.error(error instanceof Error ? error.message : 'Unable to save ingredient');
    } finally {
      savingKey = null;
    }
  }

  // ── New ingredient composer ──────────────────────────────────────
  function blankNew() {
    return {
      name: '',
      unit_of_measure: '',
      kg_per_unit: '' as number | string,
      status: 'active',
      rounding_decimals: 2,
      category: ''
    };
  }
  let showNew = $state(false);
  let newIngredient = $state(blankNew());

  function openNew() {
    newIngredient = blankNew();
    showNew = true;
  }
  function cancelNew() {
    showNew = false;
  }

  async function createIngredient() {
    if (!newIngredient.name.trim()) return toast.error('Ingredient needs a name.');
    if (!newIngredient.unit_of_measure.trim()) return toast.error('Ingredient needs a unit of measure.');
    if (!(Number(newIngredient.kg_per_unit) > 0)) return toast.error('Kg per unit must be greater than zero.');
    savingKey = 'create';
    try {
      const created = await api.createEditorIngredient({
        name: newIngredient.name.trim(),
        unit_of_measure: newIngredient.unit_of_measure.trim(),
        kg_per_unit: Number(newIngredient.kg_per_unit),
        status: newIngredient.status,
        rounding_decimals: Number(newIngredient.rounding_decimals),
        category: newIngredient.category.trim() || null
      });
      rows = [toEditable(created), ...rows];
      toast.success('Ingredient added');
      newIngredient = blankNew();
      showNew = false;
      table.reset();
    } catch (error) {
      toast.error(error instanceof Error ? error.message : 'Unable to add ingredient');
    } finally {
      savingKey = null;
    }
  }

  function clearFilters() {
    query = '';
    statusFilter = 'active';
    table.reset();
  }

  const visibleRows = $derived(
    rows.filter((row) => {
      const term = query.trim().toLowerCase();
      const statusMatches =
        statusFilter === 'all' ||
        (statusFilter === 'active' && isActive(row.status)) ||
        (statusFilter === 'archived' && !isActive(row.status));
      if (!statusMatches) return false;
      if (!term) return true;
      return [row.name, row.unit_of_measure, row.category ?? ''].join(' ').toLowerCase().includes(term);
    })
  );

  // Existing categories, offered as autocomplete suggestions so spelling stays
  // consistent across ingredients.
  const knownCategories = $derived(
    Array.from(
      new Set(
        rows
          .map((row) => (row.draft_category || row.category || '').trim())
          .filter((value) => value.length > 0)
      )
    ).sort((a, b) => a.localeCompare(b))
  );

  const table = new TableController<EditableIngredient>(() => visibleRows, {
    name: (row) => row.name,
    category: (row) => row.category ?? '',
    unit_of_measure: (row) => row.unit_of_measure,
    kg_per_unit: (row) => row.kg_per_unit,
    cost_per_kg: (row) => row.cost_per_kg,
    rounding_decimals: (row) => row.rounding_decimals,
    usage_count: (row) => row.usage_count,
    status: (row) => row.status
  });

  const dirtyCount = $derived(rows.filter(rowDirty).length);
  const inUseCount = $derived(visibleRows.filter((row) => row.usage_count > 0).length);
  const filtersActive = $derived(Boolean(query.trim() || statusFilter !== 'active'));

  // Jump back to the first page whenever the filtered set changes.
  $effect(() => {
    query;
    statusFilter;
    table.pageSize;
    table.reset();
  });
</script>

<AppSecondaryRailLayout>
  {#snippet rail()}
    <div class="filter-rail" aria-label="Ingredients Editor filters">
      <p class="rail-label">Ingredients Editor</p>

      <div class="rail-identity">
        <div class="rail-avatar" aria-hidden="true">
          <ListFilter size={16} strokeWidth={1.8} />
        </div>
        <div class="rail-identity-text">
          <p class="identity-name">Filter ingredients</p>
          <p class="identity-role">{visibleRows.length} matching rows</p>
        </div>
      </div>

      <div class="filter-rail-body">
        <label class="filter-search">
          <span>Search</span>
          <div class="search-input">
            <Search size={17} strokeWidth={2.2} />
            <input bind:value={query} type="search" placeholder="Name, unit" />
          </div>
        </label>

        <div class="status-filter" role="group" aria-label="Status filter">
          <span class="field-label">Status</span>
          <div class="segmented-control">
            <button type="button" class:active={statusFilter === 'active'} aria-pressed={statusFilter === 'active'} onclick={() => (statusFilter = 'active')}>Active</button>
            <button type="button" class:active={statusFilter === 'archived'} aria-pressed={statusFilter === 'archived'} onclick={() => (statusFilter = 'archived')}>Archived</button>
            <button type="button" class:active={statusFilter === 'all'} aria-pressed={statusFilter === 'all'} onclick={() => (statusFilter = 'all')}>All</button>
          </div>
        </div>

        {#if filtersActive}
          <button type="button" class="clear-button rail-clear" onclick={clearFilters}>
            <X size={16} strokeWidth={2.4} /> Clear filters
          </button>
        {/if}
      </div>
    </div>
  {/snippet}

  <section class="editor">
    <div class="status-band">
      <div class="editor-status">
        <span>
          <strong>Ingredients Editor</strong>
          <small>Curate the raw materials available to mixes</small>
        </span>
      </div>

      <dl class="facts">
        <div class="fact">
          <dt>Ingredients</dt>
          <dd>{visibleRows.length}</dd>
        </div>
        <div class="fact">
          <dt>In use</dt>
          <dd>{inUseCount}</dd>
        </div>
        <div class="fact">
          <dt>Unsaved</dt>
          <dd>{dirtyCount}</dd>
        </div>
      </dl>

      <button type="button" class="apply-button new-button" onclick={openNew}>
        <Plus size={16} strokeWidth={2.4} /> New ingredient
      </button>
    </div>

    {#if showNew}
      <div class="new-panel" transition:fade={{ duration: 120 }}>
        <div class="new-grid">
          <label>
            <span>Name</span>
            <input bind:value={newIngredient.name} placeholder="e.g. Maize" />
          </label>
          <label>
            <span>Unit of measure</span>
            <input bind:value={newIngredient.unit_of_measure} placeholder="kg, tonne, 20kg bag" />
          </label>
          <label>
            <span>Kg per unit</span>
            <input bind:value={newIngredient.kg_per_unit} type="number" min="0" step="0.0001" placeholder="0" />
          </label>
          <label>
            <span>Category</span>
            <input bind:value={newIngredient.category} list="ingredient-categories" placeholder="e.g. Grains" />
          </label>
          <label>
            <span>Rounding</span>
            <select bind:value={newIngredient.rounding_decimals}>
              {#each ROUNDING_OPTIONS as dp}
                <option value={dp}>{dp} dp</option>
              {/each}
            </select>
          </label>
          <label>
            <span>Status</span>
            <select bind:value={newIngredient.status}>
              <option value="active">Active</option>
              <option value="archived">Archived</option>
            </select>
          </label>
        </div>
        <div class="new-actions">
          <button type="button" class="clear-button" onclick={cancelNew}>Cancel</button>
          <button type="button" class="apply-button" disabled={savingKey === 'create'} onclick={createIngredient}>
            {savingKey === 'create' ? 'Adding...' : 'Add ingredient'}
          </button>
        </div>
      </div>
    {/if}

    <div class="pagination-bar" aria-label="Ingredient table pagination">
      <span>{table.pageStart}-{table.pageEnd} of {table.total}</span>
      <label class="page-size">
        <span>Rows</span>
        <select bind:value={table.pageSize}>
          {#each table.pageSizes as size}
            <option value={size}>{size}</option>
          {/each}
        </select>
      </label>
      <div class="page-controls">
        <button type="button" class="clear-button icon-button" disabled={!table.canPrev} onclick={() => table.prev()} aria-label="Previous page">
          <ChevronLeft size={16} strokeWidth={2.4} />
        </button>
        <span>Page {table.currentPage} of {table.totalPages}</span>
        <button type="button" class="clear-button icon-button" disabled={!table.canNext} onclick={() => table.next()} aria-label="Next page">
          <ChevronRight size={16} strokeWidth={2.4} />
        </button>
      </div>
    </div>

    <div class="log">
      <div class="log-head">
        <SortHeader label="Ingredient" column="name" controller={table} />
        <SortHeader label="Category" column="category" controller={table} />
        <SortHeader label="Unit" column="unit_of_measure" controller={table} />
        <SortHeader label="Kg / unit" column="kg_per_unit" controller={table} />
        <SortHeader label="Cost / kg" column="cost_per_kg" controller={table} />
        <SortHeader label="Rounding" column="rounding_decimals" controller={table} />
        <SortHeader label="Used in" column="usage_count" controller={table} />
        <SortHeader label="Status" column="status" controller={table} />
        <span>Actions</span>
      </div>

      {#each table.rows as row (row.id)}
        <div class="row" class:edited={rowDirty(row)}>
          <div class="cell">
            <span class="cell-label">Ingredient</span>
            <input bind:value={row.draft_name} aria-label="Ingredient name" />
          </div>

          <div class="cell">
            <span class="cell-label">Category</span>
            <input bind:value={row.draft_category} list="ingredient-categories" placeholder="—" aria-label="Category" />
          </div>

          <div class="cell">
            <span class="cell-label">Unit</span>
            <input bind:value={row.draft_unit_of_measure} aria-label="Unit of measure" />
          </div>

          <div class="cell">
            <span class="cell-label">Kg / unit</span>
            <input bind:value={row.draft_kg_per_unit} type="number" min="0" step="0.0001" aria-label="Kg per unit" />
          </div>

          <div class="cell">
            <span class="cell-label">Cost / kg</span>
            <span class="readonly-value cost-value">{row.cost_per_kg != null ? `$${formatNumber(row.cost_per_kg, 4)}` : '—'}</span>
          </div>

          <div class="cell">
            <span class="cell-label">Rounding</span>
            <select bind:value={row.draft_rounding_decimals} aria-label="Mix calculator rounding (decimal places)">
              {#each ROUNDING_OPTIONS as dp}
                <option value={dp}>{dp} dp</option>
              {/each}
            </select>
          </div>

          <div class="cell">
            <span class="cell-label">Used in</span>
            <span class="usage-value">{row.usage_count} {row.usage_count === 1 ? 'mix' : 'mixes'}</span>
          </div>

          <div class="cell">
            <span class="cell-label">Status</span>
            <label class="status-toggle" class:on={isActive(row.draft_status)}>
              <input
                type="checkbox"
                checked={isActive(row.draft_status)}
                onchange={(event) => (row.draft_status = event.currentTarget.checked ? 'active' : 'archived')}
              />
              <span>{isActive(row.draft_status) ? 'Active' : 'Archived'}</span>
            </label>
          </div>

          <div class="row-actions">
            <button class="clear-button" type="button" onclick={() => (historyIngredient = row)} aria-label={`History for ${row.name}`}>
              <History size={16} strokeWidth={2.2} />
              History
            </button>
            <button class="apply-button" type="button" disabled={!rowDirty(row) || savingKey === `row:${row.id}`} onclick={() => saveRow(row)}>
              <Save size={16} strokeWidth={2.4} />
              {savingKey === `row:${row.id}` ? 'Saving...' : 'Save'}
            </button>
            {#if rowDirty(row)}
              <button class="link-button" type="button" onclick={() => resetRow(row)}>Reset</button>
            {/if}
          </div>
        </div>
      {:else}
        <div class="empty">
          <div class="empty-copy">
            <FlaskConical size={20} strokeWidth={1.9} />
            <p>{rows.length ? 'No ingredients match your search' : 'No ingredients yet'}</p>
          </div>
          {#if filtersActive}
            <button type="button" class="clear-button" onclick={clearFilters}>Clear filters</button>
          {:else}
            <button type="button" class="apply-button" onclick={openNew}>
              <Plus size={16} strokeWidth={2.4} /> New ingredient
            </button>
          {/if}
        </div>
      {/each}
    </div>
  </section>

  <datalist id="ingredient-categories">
    {#each knownCategories as category (category)}
      <option value={category}></option>
    {/each}
  </datalist>

  {#if historyIngredient}
    <ChangeHistoryModal
      entityType="ingredient"
      entityId={historyIngredient.id}
      title={historyIngredient.name}
      subtitle={historyIngredient.unit_of_measure}
      onClose={() => (historyIngredient = null)}
    />
  {/if}
</AppSecondaryRailLayout>

<style>
  .editor {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    min-height: 100%;
    padding: 1rem 1.15rem 2rem;
    background: var(--color-bg-app);
  }

  :global(.secondary-rail-layout) {
    margin-bottom: 1.25rem;
  }

  :global(.secondary-rail-layout-panel),
  :global(.secondary-rail-layout-content) {
    background: var(--color-bg-app);
  }

  .filter-rail {
    position: sticky;
    top: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    height: 100%;
    min-height: calc(100vh - 8.5rem);
    background: var(--color-bg-surface);
    border-right: 1px solid var(--line);
    overflow-y: auto;
  }

  .rail-label {
    margin: 0;
    padding: 1rem 1rem 0.15rem;
    color: var(--color-text-muted);
    font-size: 0.64rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
  }

  .rail-identity {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0 1rem 1.15rem;
    border-bottom: 1px solid color-mix(in srgb, var(--line) 78%, transparent);
  }

  .rail-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 2.15rem;
    height: 2.15rem;
    border: 1px solid color-mix(in srgb, var(--color-brand) 22%, var(--color-border));
    border-radius: 50%;
    background: var(--color-brand-tint);
    color: var(--color-brand);
  }

  .rail-identity-text {
    min-width: 0;
  }

  .identity-name,
  .identity-role {
    margin: 0;
  }

  .identity-name {
    color: var(--color-text-primary);
    font-size: 0.8rem;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .identity-role {
    color: var(--color-text-muted);
    font-size: 0.72rem;
  }

  .filter-rail-body {
    display: grid;
    gap: 0.85rem;
    padding: 0.8rem 0.8rem 1rem;
  }

  .rail-clear {
    width: 100%;
  }

  .status-band {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem 2rem;
    flex-wrap: wrap;
    padding: 0.9rem 1.1rem;
    background: var(--color-brand-tint);
    border: 1px solid color-mix(in srgb, var(--color-brand) 32%, var(--color-border));
    border-radius: 0.9rem;
  }

  .editor-status {
    display: flex;
    align-items: center;
    gap: 0.6rem;
  }

  .editor-status span {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
  }

  .editor-status strong {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-text-primary);
  }

  .editor-status small {
    color: var(--color-text-secondary);
    font-size: 0.88rem;
  }

  .facts {
    display: flex;
    gap: 1.35rem;
    margin: 0;
  }

  .fact {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    text-align: right;
  }

  .fact dt {
    color: var(--color-text-secondary);
    font-size: 0.78rem;
    font-weight: 500;
  }

  .fact dd {
    margin: 0;
    color: var(--color-text-primary);
    font-size: 1.25rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }

  .new-button {
    margin-left: auto;
  }

  .new-panel {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    padding: 1rem;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: 0.9rem;
  }

  .new-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 0.7rem;
  }

  .new-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
  }

  .row-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: nowrap;
    justify-content: flex-end;
  }

  .clear-button,
  .apply-button,
  .link-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    min-height: 34px;
    border-radius: 0.45rem;
    padding: 0.45rem 0.65rem;
    font-size: 0.86rem;
    font-weight: 650;
    cursor: pointer;
  }

  .clear-button {
    color: var(--color-text-primary);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
  }

  .clear-button:hover {
    border-color: var(--color-text-muted);
  }

  .clear-button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .apply-button {
    color: var(--color-on-brand);
    background: var(--color-brand);
    border: 1px solid var(--color-brand);
  }

  .apply-button:disabled {
    opacity: 0.55;
    cursor: not-allowed;
  }

  .link-button {
    color: var(--color-text-secondary);
    background: transparent;
    border: 0;
    padding-inline: 0.2rem;
  }

  .field-label {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    color: var(--color-text-muted);
    font-size: 0.76rem;
    font-weight: 700;
  }

  .pagination-bar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.8rem;
    flex-wrap: wrap;
    color: var(--color-text-secondary);
    font-size: 0.84rem;
    font-weight: 600;
  }

  .page-size {
    flex-direction: row;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.84rem;
  }

  .page-size select {
    width: 4.6rem;
    min-height: 32px;
    padding-block: 0.25rem;
  }

  .page-controls {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
  }

  .icon-button {
    width: 32px;
    min-height: 32px;
    padding: 0;
  }

  .filter-search {
    min-width: 0;
  }

  .status-filter {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
  }

  .segmented-control {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    min-height: 34px;
    padding: 0.18rem;
    border: 1px solid var(--color-border);
    border-radius: 0.42rem;
    background: var(--color-bg-app);
  }

  .segmented-control button {
    min-width: 0;
    border: 0;
    border-radius: 0.32rem;
    background: transparent;
    color: var(--color-text-secondary);
    font-size: 0.82rem;
    font-weight: 700;
    cursor: pointer;
  }

  .segmented-control button:hover {
    color: var(--color-text-primary);
    background: var(--color-bg-surface);
  }

  .segmented-control button.active {
    color: var(--color-brand);
    background: var(--color-bg-surface);
    box-shadow: 0 0 0 1px var(--color-border);
  }

  label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    color: var(--color-text-primary);
    font-size: 0.9rem;
    font-weight: 500;
  }

  label span,
  .cell-label {
    color: var(--color-text-muted);
    font-size: 0.76rem;
    font-weight: 650;
  }

  .search-input {
    position: relative;
    display: flex;
    align-items: center;
  }

  .search-input :global(svg) {
    position: absolute;
    left: 0.58rem;
    color: var(--color-text-muted);
    pointer-events: none;
  }

  .search-input input {
    padding-left: 1.95rem;
  }

  input,
  select {
    width: 100%;
    min-height: 36px;
    border: 1px solid var(--color-border);
    border-radius: 0.42rem;
    background: var(--color-bg-surface);
    color: var(--color-text-primary);
    padding: 0.38rem 0.5rem;
    font-size: 0.88rem;
  }

  select {
    cursor: pointer;
  }

  input::placeholder {
    color: var(--color-text-muted);
  }

  input:hover,
  select:hover {
    border-color: var(--color-text-muted);
  }

  input:focus-visible,
  select:focus-visible,
  button:focus-visible {
    outline: 3px solid var(--color-brand);
    outline-offset: 2px;
    border-color: var(--color-brand);
  }

  .log {
    display: flex;
    flex-direction: column;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: 0.9rem;
    overflow: hidden;
  }

  .log-head,
  .row {
    display: grid;
    grid-template-columns:
      minmax(160px, 1.3fr)
      minmax(104px, 0.7fr)
      minmax(90px, 0.6fr)
      minmax(88px, 0.5fr)
      minmax(92px, 0.5fr)
      minmax(82px, 0.45fr)
      minmax(82px, 0.45fr)
      minmax(104px, 0.55fr)
      minmax(150px, auto);
    gap: 0.55rem;
    align-items: center;
  }

  .log-head {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 0.55rem 0.85rem;
    background: var(--color-bg-app);
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text-secondary);
    font-size: 0.76rem;
    font-weight: 650;
    text-transform: uppercase;
  }

  .row {
    padding: 0.58rem 0.85rem;
    border-bottom: 1px solid var(--color-divider);
    transition: background-color 140ms ease;
  }

  .row:hover {
    background: var(--color-surface-hover);
  }

  .row.edited {
    background: var(--color-brand-tint);
  }

  .cell {
    display: flex;
    flex-direction: column;
    gap: 0.22rem;
    min-width: 0;
  }

  .readonly-value {
    display: flex;
    align-items: center;
    min-height: 34px;
    color: var(--color-text-primary);
    font-size: 0.88rem;
    font-weight: 650;
  }

  .cost-value {
    font-variant-numeric: tabular-nums;
  }

  .usage-value {
    display: flex;
    align-items: center;
    min-height: 34px;
    color: var(--color-text-secondary);
    font-size: 0.84rem;
    font-weight: 600;
  }

  .cell-label {
    display: none;
  }

  .status-toggle {
    display: inline-flex;
    align-items: center;
    flex-direction: row;
    justify-content: flex-start;
    gap: 0.3rem;
    min-height: 34px;
    padding: 0.25rem 0;
    border: 1px solid transparent;
    border-radius: 0.42rem;
    background: transparent;
    color: var(--color-text-secondary);
    font-size: 0.76rem;
    font-weight: 600;
    cursor: pointer;
    user-select: none;
  }

  .status-toggle input {
    width: 0.85rem;
    min-height: 0.85rem;
    margin: 0;
    accent-color: var(--color-brand);
  }

  .status-toggle span {
    color: inherit;
    font-size: inherit;
    font-weight: inherit;
  }

  .status-toggle.on {
    color: var(--color-success);
  }

  .empty {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.5rem;
    color: var(--color-text-secondary);
  }

  .empty-copy {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
  }

  .empty-copy p {
    margin: 0;
    font-weight: 600;
  }

  @media (max-width: 1180px) {
    .log-head {
      display: none;
    }

    .row {
      grid-template-columns: 1fr 1fr;
    }

    .cell-label {
      display: block;
    }

    .row-actions {
      grid-column: 1 / -1;
      justify-content: flex-start;
    }
  }

  @media (max-width: 980px) {
    .filter-rail {
      position: static;
      min-height: auto;
      height: auto;
      border-right: none;
      overflow: visible;
    }

    .filter-rail-body {
      grid-template-columns: repeat(2, minmax(0, 1fr));
      align-items: end;
    }

    .filter-search {
      grid-column: 1 / -1;
    }
  }

  @media (max-width: 760px) {
    .filter-rail-body {
      grid-template-columns: 1fr;
    }

    .row {
      grid-template-columns: 1fr;
    }

    .facts {
      width: 100%;
      justify-content: space-between;
      gap: 1rem;
    }

    .fact {
      text-align: left;
    }

    .new-button {
      margin-left: 0;
    }
  }
</style>
