{
  "version": 3,
  "sources": ["src/frontend/ui/html.ts", "src/frontend/i18n/de.ts", "src/frontend/i18n/en.ts", "src/frontend/ui/storage-keys.ts", "src/frontend/ui/i18n.ts", "src/frontend/ui/inline-styles.ts", "src/frontend/ui/styles.ts", "src/frontend/ui/base.ts", "src/frontend/http/errors.ts", "src/frontend/ui/toast.ts", "src/frontend/ui/view.ts", "src/frontend/app/router.ts", "src/frontend/http/http.ts", "src/frontend/features/auth/internal/auth.api.ts", "src/frontend/features/auth/auth.facade.ts", "src/frontend/features/auth/internal/next-path.ts", "src/frontend/features/auth/ce-login.component.ts", "src/frontend/ui/modal.ts", "src/frontend/ui/confirm/ce-confirm-dialog.component.ts", "src/frontend/ui/confirm/confirm.ts", "src/frontend/ui/pagination/pagination.ts", "src/frontend/ui/pagination/ce-pagination.component.ts", "src/frontend/features/tags/internal/tag.api.ts", "src/frontend/features/tags/tags.facade.ts", "src/frontend/features/recipes/internal/list-query.ts", "src/frontend/ui/format.ts", "src/frontend/features/recipes/internal/ce-recipe-card.component.ts", "src/frontend/features/recipes/internal/jaro-winkler.ts", "src/frontend/features/recipes/internal/tag-filter.ts", "src/frontend/features/recipes/internal/ce-tag-panel.component.ts", "src/frontend/features/recipes/internal/recipe.api.ts", "src/frontend/features/recipes/recipes.facade.ts", "src/frontend/features/recipes/ce-recipe-list.component.ts", "src/frontend/ui/leave-guard.ts", "src/frontend/features/recipes/internal/history-time.ts", "src/frontend/features/recipes/internal/ce-history-field.component.ts", "src/frontend/features/recipes/internal/ce-list-field.component.ts", "src/frontend/features/recipes/ce-recipe-edit.component.ts", "src/frontend/features/recipes/ce-recipe-import.component.ts", "src/frontend/features/recipes/internal/portions.ts", "src/frontend/features/recipes/internal/ce-ingredients.component.ts", "src/frontend/features/recipes/internal/ce-rating.component.ts", "src/frontend/features/recipes/internal/wake-lock.ts", "src/frontend/features/recipes/ce-recipe-detail.component.ts", "node_modules/@knadh/oat/oat.min.js", "src/frontend/app/ce-app.component.ts", "src/frontend/features/auth/auth.routes.ts", "src/frontend/features/recipes/recipes.routes.ts", "src/frontend/app/routes.ts", "src/frontend/app/tab-title.ts", "src/frontend/app/ce-breadcrumbs.component.ts", "src/frontend/app/ce-header.component.ts", "src/frontend/app/ce-language-switch.component.ts", "src/frontend/app/ce-theme-toggle.component.ts", "src/frontend/ui/theme.ts"],
  "sourcesContent": [
    "export class RawHtml {\n  readonly value: string;\n\n  constructor(value: string) {\n    this.value = value;\n  }\n\n  toString(): string {\n    return this.value;\n  }\n}\n\nexport const raw = (value: unknown): RawHtml => new RawHtml(String(value));\n\nconst ESCAPES: Record<string, string> = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' };\n\nexport function escapeHtml(value: unknown): string {\n  return String(value).replace(/[&<>\"']/g, (char) => ESCAPES[char] ?? char);\n}\n\nfunction interpolate(value: unknown): string {\n  if (value === null || value === undefined || value === false) {\n    return '';\n  }\n  if (value instanceof RawHtml) {\n    return value.value;\n  }\n  if (Array.isArray(value)) {\n    return value.map(interpolate).join('');\n  }\n  return escapeHtml(value);\n}\n\nexport function html(strings: TemplateStringsArray, ...values: unknown[]): RawHtml {\n  let out = strings[0] ?? '';\n  for (let i = 0; i < values.length; i++) {\n    out += interpolate(values[i]) + (strings[i + 1] ?? '');\n  }\n  return raw(out);\n}\n",
    "import type { Language } from './language.ts';\n\nexport const de: Language = {\n  appName: 'Cena',\n  cancel: 'Abbrechen',\n  confirmation: 'Bestätigung',\n  create: 'Erstellen',\n  createdAt: 'Erstellt',\n  delete: 'Löschen',\n  description: 'Beschreibung',\n  down: 'Unten',\n  edit: 'Bearbeiten',\n  error: {\n    404: {\n      description: 'Die gesuchte Seite kann nicht gefunden werden.',\n      title: 'Nicht gefunden',\n    },\n    500: {\n      description: 'Es ist ein Serverfehler aufgetreten.',\n      title: 'Serverfehler',\n    },\n    title: 'Fehler',\n  },\n  id: 'ID',\n  info: 'Info',\n  navigation: {\n    filterPlaceholder: 'Beginne zu Tippen um zu filtern...',\n    filterTitle: 'Filter',\n    tags: 'Tags',\n  },\n  no: 'Nein',\n  orderBy: {\n    asc: 'Wechsel zu aufsteigender Reihenfolge',\n    desc: 'Wechsel zu absteigender Reihenfolge',\n    title: 'Sortiere nach',\n  },\n  pagination: {\n    next: 'Nächste Seite',\n    previous: 'Vorherige Seite',\n    page: (page) => `Seite ${page}`,\n    emptyPage: 'Auf dieser Seite gibt es nichts.',\n    firstPage: 'Zu Seite 1',\n  },\n  recipe: {\n    count: (n) => `${n} Rezept${n === 1 ? '' : 'e'}`,\n    newRecipe: 'Neues Rezept',\n    recipe: 'Rezept',\n    deleteTitle: (title) => `„${title}“ löschen?`,\n    ratingCount: (n) => `${n} Bewertung${n === 1 ? '' : 'en'}`,\n    noRating: 'Noch nicht bewertet',\n    actions: 'Rezeptaktionen',\n    cookedTimes: (n) => `${n}× gekocht`,\n    tagsSelected: (n) => (n === 0 ? 'Tags' : `Tags (${n} ausgewählt)`),\n    noTags: 'Noch keine Tags.',\n    aggregateRating: 'Fremde Bewertung',\n    aggregateRatingCount: 'Anzahl fremder Bewertungen',\n    aggregateRatingValue: 'Fremde Bewertung',\n    clearAllTags: 'Lösche alle',\n    cookTime: 'Kochzeit',\n    cookedCount: 'Kochanzahl',\n    createSuccessful: 'Das Rezept wurde erfolgreich angelegt.',\n    deleteConfirmation: 'Soll dieses Rezept wirklich gelöscht werden?',\n    deleteSuccessful: 'Das Rezept wurde erfolgreich gelöscht.',\n    editSuccessful: 'Das Rezept wurde erfolgreich bearbeitet.',\n    form: {\n      addHistoryEntry: 'Neuen Eintrag hinzufügen',\n      createIngredient: 'Neue Zutat',\n      createInstruction: 'Neue Anweisung',\n      deleteThumbnail: 'Lösche Vorschaubild',\n      group: {\n        basic: 'Allgemein',\n        nutrition: 'Nährwerte',\n        ratings: 'Bewertungen',\n        times: 'Zeiten',\n      },\n      imageError: 'Es muss ein gültiges Bild ausgewählt werden.',\n      thumbnailHint: 'Hier kann geklickt oder ein Bild hineingezogen werden.',\n      timeHint: 'Zeit in Minuten.',\n      sourceUrl: 'Quell-URL',\n      thumbnail: 'Vorschaubild',\n      imageTooLarge: 'Das Bild darf höchstens 10 MB groß sein.',\n      removeThumbnail: 'Entfernen',\n      tagsPlaceholder: 'Tag hinzufügen…',\n      date: 'Datum',\n      time: 'Uhrzeit',\n      remove: 'Entfernen',\n      ingredient: (n) => `Zutat ${n}`,\n      instruction: (n) => `Anweisung ${n}`,\n      historyEntry: (n) => `Eintrag ${n}`,\n      saved: 'Rezept gespeichert',\n      required: 'Pflichtfeld',\n    },\n    import: {\n      alert: 'Die URLs wurden verarbeitet und die Ergebnisse sind folgend zu entnehmen.',\n      result: 'Ergebnis',\n      sourceUrl: 'Quell-URL',\n      title: 'Import',\n      urlInfo: 'Eine URL pro Zeile.',\n      urls: 'URLs',\n    },\n    ingredients: {\n      step: (step) => `Schritt ${step}`,\n      title: 'Zutaten',\n    },\n    instructions: 'Anweisungen',\n    lastCooked: (distance) => `Zuletzt gekocht ${distance}.`,\n    lastCookedAt: 'Zuletzt gekocht am',\n    flagged: 'Gekennzeichnet',\n    flag: 'Kennzeichnung hinzufügen',\n    unflag: 'Kennzeichnung entfernen',\n    noRecipesFound: 'Für den aktuell gewählten Filter wurden keine Rezepte gefunden. Bitte die Auswahl ändern oder neue Rezepte hinzufügen.',\n    notCookedYet: 'Noch nicht gekocht.',\n    noTimes: 'Es sind keine Zeiten hinterlegt.',\n    nutrition: {\n      calories: 'Kalorien',\n      carbohydrate: 'Kohlenhydrate',\n      cholesterol: 'Cholesterin',\n      fat: 'Fett',\n      fiber: 'Ballaststoffe',\n      protein: 'Proteine',\n      saturatedFat: 'Gesättigte Fettsäuren',\n      sodium: 'Natrium',\n      sugar: 'Zucker',\n      transFat: 'Transfettsäuren',\n      unsaturatedFat: 'Ungesättigte Fettsäuren',\n    },\n    open: 'Öffnen',\n    orderBy: 'Sortiere nach',\n    portions: 'Portionen',\n    prepTime: 'Vorbereitungszeit',\n    rating: 'Deine Bewertung',\n    reviews: 'Rezensionen',\n    source: 'Quelle',\n    time: {\n      cook: 'Kochzeit',\n      prep: 'Vorbereitungszeit',\n      total: 'Gesamtzeit',\n    },\n    yield: 'Gesamtportionen',\n    history: 'Historie',\n  },\n  recipes: 'Rezepte',\n  save: 'Speichern',\n  search: 'Suchen',\n  title: 'Titel',\n  up: 'Hoch',\n  updatedAt: 'Aktualisiert',\n  yes: 'Ja',\n  darkMode: 'Nachtmodus',\n  lightMode: 'Tagmodus',\n  language: 'Sprache',\n  nutritionalValue: 'Nährwertangaben',\n  languageNames: {\n    de: 'Deutsch',\n    en: 'Englisch',\n  },\n  notFound: (path) => `Unter ${path} gibt es nichts.`,\n  loading: 'Lädt…',\n  leave: {\n    title: 'Änderungen verwerfen?',\n    message: 'Die Änderungen an diesem Rezept wurden nicht gespeichert.',\n    discard: 'Verwerfen',\n  },\n  logOut: 'Abmelden',\n  menu: 'Menü',\n  theme: {\n    turnOnDark: 'Nachtmodus einschalten',\n    turnOffDark: 'Nachtmodus ausschalten',\n  },\n  login: {\n    title: 'Anmelden',\n    password: 'Passwort',\n    placeholder: 'Dein Passwort',\n    submit: 'Anmelden',\n    wrongPassword: 'Falsches Passwort.',\n  },\n  meta: {\n    id: 'de',\n    name: 'Deutsch',\n    bcp47: 'de-DE',\n  },\n} as const;\n",
    "import type { Language } from './language.ts';\n\nexport const en: Language = {\n  appName: 'Cena',\n  cancel: 'Cancel',\n  confirmation: 'Confirmation',\n  create: 'Create',\n  createdAt: 'Created',\n  delete: 'Delete',\n  description: 'Description',\n  down: 'Down',\n  edit: 'Edit',\n  error: {\n    404: {\n      description: 'The page you are looking for can not be found.',\n      title: 'Not Found',\n    },\n    500: {\n      description: 'A server error occurred.',\n      title: 'Server Error',\n    },\n    title: 'Error',\n  },\n  id: 'ID',\n  info: 'Info',\n  navigation: {\n    filterPlaceholder: 'Start typing to filter...',\n    filterTitle: 'Filter',\n    tags: 'Tags',\n  },\n  no: 'No',\n  orderBy: {\n    asc: 'Switch to ascending order',\n    desc: 'Switch to descending order',\n    title: 'Order by',\n  },\n  pagination: {\n    next: 'Next page',\n    previous: 'Previous page',\n    page: (page) => `Page ${page}`,\n    emptyPage: 'Nothing on this page.',\n    firstPage: 'Go to page 1',\n  },\n  recipe: {\n    count: (n) => `${n} Recipe${n === 1 ? '' : 's'}`,\n    newRecipe: 'New recipe',\n    recipe: 'Recipe',\n    deleteTitle: (title) => `Delete \"${title}\"?`,\n    ratingCount: (n) => `${n} rating${n === 1 ? '' : 's'}`,\n    noRating: 'Not rated yet',\n    actions: 'Recipe actions',\n    cookedTimes: (n) => `${n}× cooked`,\n    tagsSelected: (n) => (n === 0 ? 'Tags' : `Tags (${n} selected)`),\n    noTags: 'No tags yet.',\n    aggregateRating: 'Foreign rating',\n    aggregateRatingCount: 'Number of foreign ratings',\n    aggregateRatingValue: 'Foreign rating',\n    clearAllTags: 'Clear all',\n    cookTime: 'Cook time',\n    cookedCount: 'Cooked count',\n    createSuccessful: 'The Recipe has been created successfully.',\n    deleteConfirmation: 'Are you sure you want to delete this Recipe?',\n    deleteSuccessful: 'The Recipe has been deleted successfully.',\n    editSuccessful: 'The Recipe has been edited successfully.',\n    form: {\n      addHistoryEntry: 'Create History entry',\n      createIngredient: 'Create Ingredient',\n      createInstruction: 'Create Instruction',\n      deleteThumbnail: 'Delete thumbnail',\n      group: {\n        basic: 'Basic',\n        nutrition: 'Nutrition',\n        ratings: 'Ratings',\n        times: 'Times',\n      },\n      imageError: 'Please make sure you select an image.',\n      thumbnailHint: 'You may click or drag and drop an image here.',\n      timeHint: 'Time in minutes.',\n      sourceUrl: 'Source URL',\n      thumbnail: 'Thumbnail',\n      imageTooLarge: 'The image must be at most 10 MB.',\n      removeThumbnail: 'Remove',\n      tagsPlaceholder: 'Add a tag…',\n      date: 'Date',\n      time: 'Time',\n      remove: 'Remove',\n      ingredient: (n) => `Ingredient ${n}`,\n      instruction: (n) => `Instruction ${n}`,\n      historyEntry: (n) => `Entry ${n}`,\n      saved: 'Recipe saved',\n      required: 'Required',\n    },\n    import: {\n      alert: 'The URLs have been processed. Please find the results below.',\n      result: 'Result',\n      sourceUrl: 'Source URL',\n      title: 'Import',\n      urlInfo: 'One URL per line.',\n      urls: 'URLs',\n    },\n    ingredients: {\n      step: (step) => `Step ${step}`,\n      title: 'Ingredients',\n    },\n    instructions: 'Instructions',\n    lastCooked: (distance) => `Last cooked ${distance}.`,\n    lastCookedAt: 'Last cooked at',\n    flagged: 'Flagged',\n    flag: 'Flag',\n    unflag: 'Remove flag',\n    noRecipesFound: 'No recipes have been found for the current filter. Please change the filter options or add some more recipes.',\n    notCookedYet: 'Not cooked yet.',\n    noTimes: 'No times have been set for this recipe.',\n    nutrition: {\n      calories: 'Calories',\n      carbohydrate: 'Carbohydrate',\n      cholesterol: 'Cholesterol',\n      fat: 'Fat',\n      fiber: 'Fiber',\n      protein: 'Protein',\n      saturatedFat: 'Saturated Fat',\n      sodium: 'Sodium',\n      sugar: 'Sugar',\n      transFat: 'Trans Fat',\n      unsaturatedFat: 'Unsaturated Fat',\n    },\n    open: 'Open',\n    orderBy: 'Order by',\n    portions: 'Portions',\n    prepTime: 'Prep time',\n    rating: 'Your Rating',\n    reviews: 'Reviews',\n    source: 'Source',\n    time: {\n      cook: 'Cook time',\n      prep: 'Prep time',\n      total: 'Total time',\n    },\n    yield: 'Yield',\n    history: 'History',\n  },\n  recipes: 'Recipes',\n  save: 'Save',\n  search: 'Search',\n  title: 'Title',\n  up: 'Up',\n  updatedAt: 'Updated',\n  yes: 'Yes',\n  darkMode: 'Dark Mode',\n  lightMode: 'Light Mode',\n  language: 'Language',\n  nutritionalValue: 'Nutritional Value',\n  languageNames: {\n    de: 'German',\n    en: 'English',\n  },\n  notFound: (path) => `Nothing lives at ${path}.`,\n  loading: 'Loading…',\n  leave: {\n    title: 'Discard changes?',\n    message: 'Your changes to this recipe have not been saved.',\n    discard: 'Discard',\n  },\n  logOut: 'Log out',\n  menu: 'Menu',\n  theme: {\n    turnOnDark: 'Turn on dark mode',\n    turnOffDark: 'Turn off dark mode',\n  },\n  login: {\n    title: 'Log in',\n    password: 'Password',\n    placeholder: 'Your password',\n    submit: 'Log in',\n    wrongPassword: 'Wrong password.',\n  },\n  meta: {\n    id: 'en',\n    name: 'English',\n    bcp47: 'en-US',\n  },\n} as const;\n",
    "// The `localStorage` keys. index.html's pre-paint script runs before any module and spells them out; storage-keys.test.ts keeps it in step.\nexport const THEME_STORAGE_KEY = 'cena:theme';\nexport const LANGUAGE_STORAGE_KEY = 'cena:language';\n",
    "import { de } from '../i18n/de.ts';\nimport { en } from '../i18n/en.ts';\nimport type { Language, LanguageId } from '../i18n/language.ts';\nimport { LANGUAGE_STORAGE_KEY } from './storage-keys.ts';\n\nexport const LANGUAGE_CHANGE_EVENT = 'ce-language-change';\n\nexport const LANGUAGES: Readonly<Record<LanguageId, Language>> = { en, de };\nexport const LANGUAGE_IDS: readonly LanguageId[] = ['en', 'de'];\n\nfunction isLanguageId(value: string | null | undefined): value is LanguageId {\n  return value === 'en' || value === 'de';\n}\n\nfunction browserLanguage(): LanguageId {\n  // Bun's own navigator, before a test installs a DOM, has neither field.\n  // oxlint-disable-next-line typescript/no-unnecessary-condition -- see above\n  const preferred: unknown[] = typeof navigator === 'undefined' ? [] : [...(navigator.languages ?? []), navigator.language];\n  for (const tag of preferred) {\n    const id = typeof tag === 'string' ? tag.toLowerCase().split('-')[0] : undefined;\n    if (isLanguageId(id)) {\n      return id;\n    }\n  }\n  return 'en';\n}\n\nfunction readStoredLanguage(): LanguageId {\n  try {\n    const stored = localStorage.getItem(LANGUAGE_STORAGE_KEY);\n    if (isLanguageId(stored)) {\n      return stored;\n    }\n  } catch {}\n  return browserLanguage();\n}\n\nlet current = readStoredLanguage();\n\nexport function language(): Language {\n  return LANGUAGES[current];\n}\n\nexport const t = language;\n\nexport function setLanguage(id: LanguageId): void {\n  if (id === current) {\n    return;\n  }\n  current = id;\n  try {\n    localStorage.setItem(LANGUAGE_STORAGE_KEY, id);\n  } catch {}\n  applyLanguage();\n  window.dispatchEvent(new CustomEvent(LANGUAGE_CHANGE_EVENT, { detail: { language: id } }));\n}\n\nexport function onLanguageChange(listener: () => void): () => void {\n  window.addEventListener(LANGUAGE_CHANGE_EVENT, listener);\n  return () => {\n    window.removeEventListener(LANGUAGE_CHANGE_EVENT, listener);\n  };\n}\n\nfunction applyLanguage(): void {\n  // Absent only in tests that import a formatter without a DOM.\n  if (typeof document !== 'undefined') {\n    document.documentElement.setAttribute('lang', current);\n  }\n}\n\napplyLanguage();\n",
    "export const INLINE_STYLES = {\"/ui/app.css\":\"/* Unlayered, so it wins over Oat's theme layer. ui/contrast.test.ts checks the contrast. */\\n:root {\\n  --background: light-dark(#f4f4f5, #09090b);\\n  --card: light-dark(#fff, #202024);\\n  --primary: light-dark(#2563eb, #60a5fa);\\n  --primary-foreground: light-dark(#fafafa, #09090b);\\n  --ring: light-dark(#2563eb, #60a5fa);\\n  --muted: light-dark(#e4e4e7, #27272a);\\n  --muted-foreground: light-dark(#52525b, #a1a1aa);\\n  --border: light-dark(#8b8b94, #71717a);\\n  --input: light-dark(#8b8b94, #71717a);\\n\\n  /* ce-header's fixed height, border included; sticky page chrome sits below it. */\\n  --header-height: calc(3.75rem + 1px);\\n\\n  /* Keeps the layout from shifting when a page grows tall enough to scroll. */\\n  scrollbar-gutter: stable;\\n}\\n\\n/* color-scheme inherits, so this reaches every shadow root. */\\n:root[data-theme=\\\"light\\\"] {\\n  color-scheme: light;\\n}\\n\\n:root[data-theme=\\\"dark\\\"] {\\n  color-scheme: dark;\\n}\\n\\nbody {\\n  margin: 0;\\n}\\n\\nce-app {\\n  display: block;\\n  min-height: 100vh;\\n\\n  &:not(:defined) {\\n    min-height: 100vh;\\n  }\\n}\\n\",\"/ui/shared.css\":\":host {\\n  display: block;\\n}\\n\\n/* Author styles outrank the browser's [hidden] rule, so the rule above would override it. */\\n:host([hidden]) {\\n  display: none;\\n}\\n\\n/* ------------------------------------------------------------------ layout */\\n\\n.vstack > * {\\n  margin-block: 0;\\n}\\n\\n/* Oat leaves <hgroup> unstyled. */\\nhgroup > * {\\n  margin-block: 0;\\n}\\n\\n.grow {\\n  flex: 1 1 auto;\\n  min-width: 0;\\n}\\n\\n/* ------------------------------------------------------------------- forms */\\n\\n.fields {\\n  display: flex;\\n  flex-wrap: wrap;\\n  gap: var(--space-2);\\n  align-items: flex-end;\\n\\n  & > .field {\\n    flex: 1 1 10rem;\\n    min-width: 0;\\n  }\\n}\\n\\n/* A customizable <select>, so an option can hold an icon; elsewhere it stays a plain one. */\\n@supports (appearance: base-select) {\\n  select.rich,\\n  select.rich::picker(select) {\\n    appearance: base-select;\\n  }\\n\\n  select.rich {\\n    /* Sized to the chosen option, unlike a plain select; this keeps it from jumping. */\\n    min-width: 12rem;\\n\\n    /* Oat draws the chevron as a background image. */\\n    &::picker-icon {\\n      display: none;\\n    }\\n\\n    /* Drops Oat's button look, whose white text the selected content would inherit. */\\n    & > button {\\n      all: unset;\\n      display: contents;\\n    }\\n\\n    & :is(selectedcontent, option) {\\n      display: flex;\\n      align-items: center;\\n      gap: var(--space-2);\\n    }\\n\\n    & option {\\n      padding: var(--space-2) var(--space-3);\\n    }\\n  }\\n\\n  select.rich::picker(select) {\\n    margin-block: var(--space-1);\\n    padding-block: var(--space-1);\\n    border: 1px solid var(--border);\\n    border-radius: var(--radius-medium);\\n    background-color: var(--background);\\n    color: var(--foreground);\\n    box-shadow: var(--shadow-small);\\n  }\\n}\\n\\n/* ------------------------------------------------------------------- touch */\\n\\n@media (max-width: 720px) {\\n  :is(button, a.button, select, input:not([type=\\\"checkbox\\\"], [type=\\\"radio\\\"])) {\\n    min-height: 2.75rem;\\n  }\\n\\n  :is(button, a.button).icon {\\n    min-width: 2.75rem;\\n  }\\n}\\n\\n/* ------------------------------------------------------------------ typography */\\n\\n.mono {\\n  font-family: var(--font-mono);\\n  font-variant-numeric: tabular-nums;\\n}\\n\\n.nowrap {\\n  white-space: nowrap;\\n}\\n\\n.error-text {\\n  color: var(--danger);\\n}\\n\\n/* Oat's alert display sits in a later cascade layer than its own [hidden] rule. */\\n[role=\\\"alert\\\"][hidden] {\\n  display: none;\\n}\\n\\n/* ------------------------------------------------------------------ pieces */\\n\\n.card {\\n  border-color: transparent;\\n\\n  @media (max-width: 640px) {\\n    padding: var(--space-4);\\n  }\\n}\\n\\ndetails {\\n  background-color: var(--card);\\n}\\n\\n/* The hit area is the link's own ::after, so it stays a real anchor (Ctrl-click, keyboard). */\\narticle.open-card {\\n  position: relative;\\n  display: flex;\\n  flex-direction: column;\\n  gap: var(--space-1);\\n\\n  & .head {\\n    display: flex;\\n    align-items: center;\\n    justify-content: space-between;\\n    gap: var(--space-2);\\n  }\\n\\n  & .foot {\\n    display: flex;\\n    flex-wrap: wrap;\\n    align-items: center;\\n    gap: var(--space-2);\\n\\n    & .actions {\\n      margin-inline-start: auto;\\n    }\\n  }\\n\\n  & .open {\\n    color: inherit;\\n    font-weight: 600;\\n    text-decoration: none;\\n\\n    &::after {\\n      content: \\\"\\\";\\n      position: absolute;\\n      inset: 0;\\n      border-radius: inherit;\\n    }\\n  }\\n\\n  &:hover {\\n    background-color: var(--muted);\\n  }\\n\\n  &:hover .open {\\n    color: var(--primary);\\n  }\\n\\n  /* Positioned and later in DOM order, so it paints above the overlay and takes its own click. */\\n  & .actions {\\n    position: relative;\\n    display: flex;\\n    gap: 0.25rem;\\n  }\\n}\\n\\n.empty {\\n  margin: 0;\\n  padding: var(--space-6);\\n  border: 1px dashed var(--border);\\n  border-radius: var(--radius-medium);\\n  color: var(--muted-foreground);\\n  text-align: center;\\n}\\n\\n/* ------------------------------------------------------------------ tables */\\n\\n/* Oat floors .table at 320px, wider than a card on a phone, so the page scrolled instead. */\\n.table {\\n  min-width: 0;\\n}\\n\\n:is(td, th).num {\\n  text-align: right;\\n  font-variant-numeric: tabular-nums;\\n}\\n\",\"/ui/confirm/ce-confirm-dialog.component.css\":\"/* A flex column, so a long list scrolls within Oat's max-height instead of pushing the footer out. */\\ndialog {\\n  &[open] {\\n    display: flex;\\n    flex-direction: column;\\n  }\\n\\n  & > div {\\n    min-height: 0;\\n  }\\n\\n  /* Oat leaves no gap between the header and the footer when there is no body. */\\n  & > header + footer {\\n    padding-block-start: var(--space-6);\\n  }\\n}\\n\",\"/ui/pagination/ce-pagination.component.css\":\":host {\\n  display: block;\\n}\\n\",\"/app/ce-header.component.css\":\":host {\\n  position: sticky;\\n  top: 0;\\n  z-index: 10;\\n  box-sizing: border-box;\\n  block-size: var(--header-height);\\n  background-color: var(--card);\\n  color: var(--foreground);\\n  border-bottom: 1px solid var(--border);\\n}\\n\\nnav {\\n  display: flex;\\n  align-items: center;\\n  gap: var(--space-4);\\n  padding-block: var(--space-2);\\n}\\n\\n.links {\\n  display: flex;\\n  gap: var(--space-4);\\n  margin-block: 0;\\n  margin-inline-start: auto;\\n\\n  & li {\\n    margin: 0;\\n  }\\n}\\n\\n.links a {\\n  color: var(--muted-foreground);\\n\\n  &:hover {\\n    text-decoration: underline;\\n  }\\n\\n  &[aria-current=\\\"page\\\"] {\\n    color: var(--foreground);\\n    font-weight: 600;\\n  }\\n}\\n\\n.menu {\\n  display: none;\\n  margin-inline-start: auto;\\n}\\n\\nce-theme-toggle {\\n  border-inline-start: 1px solid var(--border);\\n  padding-inline-start: var(--space-2);\\n}\\n\\n@media (max-width: 560px) {\\n  .links {\\n    display: none;\\n  }\\n\\n  /* Flex, not block, so no descender space pushes the button off-center. */\\n  .menu {\\n    display: flex;\\n    order: 1;\\n    margin-inline-start: 0;\\n    border-inline-start: 1px solid var(--border);\\n    padding-inline-start: var(--space-2);\\n  }\\n\\n  ce-language-switch {\\n    margin-inline-start: auto;\\n  }\\n\\n  ce-theme-toggle {\\n    border-inline-start: none;\\n    padding-inline-start: 0;\\n  }\\n}\\n\\n.menu button svg {\\n  width: auto;\\n  height: 1.125rem;\\n}\\n\\n/* Oat's focus background alone barely sets the current page off from the menu's. */\\nmenu[popover] a[role=\\\"menuitem\\\"] {\\n  color: var(--muted-foreground);\\n\\n  &[aria-current=\\\"page\\\"] {\\n    color: var(--foreground);\\n    font-weight: 600;\\n  }\\n}\\n\\n.brand {\\n  display: flex;\\n  align-items: center;\\n  gap: var(--space-2);\\n  text-decoration: none;\\n\\n  color: var(--primary);\\n\\n  & img {\\n    height: 1.75em;\\n    width: auto;\\n    transition: transform 150ms ease-out;\\n  }\\n\\n  & strong {\\n    letter-spacing: -0.02em;\\n  }\\n\\n  &:hover {\\n    & strong {\\n      text-decoration: underline;\\n    }\\n\\n    & img {\\n      transform: scale(1.08);\\n    }\\n  }\\n\\n  @media (prefers-reduced-motion: reduce) {\\n    & img {\\n      transition: none;\\n    }\\n  }\\n}\\n\\nnav:not(.auth) .logout {\\n  display: none;\\n}\\n\\nnav.login {\\n  & .links,\\n  & .menu {\\n    display: none;\\n  }\\n\\n  & ce-language-switch {\\n    margin-inline-start: auto;\\n  }\\n\\n  & ce-theme-toggle {\\n    border-inline-start: none;\\n    padding-inline-start: 0;\\n  }\\n}\\n\",\"/app/ce-app.component.css\":\":host {\\n  display: flex;\\n  flex-direction: column;\\n  min-height: 100vh;\\n}\\n\\nmain {\\n  flex: 1 0 auto;\\n  padding-block: var(--space-6) var(--space-12);\\n}\\n\",\"/app/ce-breadcrumbs.component.css\":\":host {\\n  margin-block-end: var(--space-4);\\n}\\n\\nol {\\n  display: flex;\\n  gap: var(--space-2);\\n  margin: 0;\\n  white-space: nowrap;\\n  color: var(--muted-foreground);\\n\\n  & li {\\n    display: flex;\\n    gap: var(--space-2);\\n    flex: none;\\n    min-width: 0;\\n    margin: 0;\\n\\n    /* Empty alt text; the first declaration is the fallback for engines without it. */\\n    & + li::before {\\n      content: \\\"›\\\";\\n      content: \\\"›\\\" / \\\"\\\";\\n    }\\n\\n    &:last-child {\\n      flex: 0 1 auto;\\n    }\\n  }\\n\\n  & [aria-current=\\\"page\\\"] {\\n    overflow: hidden;\\n    text-overflow: ellipsis;\\n    color: var(--foreground);\\n  }\\n}\\n\",\"/app/ce-theme-toggle.component.css\":\"/* Flex, not a line box, so no descender space pushes the button off-center. */\\n:host {\\n  display: flex;\\n}\\n\\n/* At every width, so the header it sets the height of matches on desktop and phone. */\\nbutton {\\n  min-height: 2.75rem;\\n  min-width: 2.75rem;\\n}\\n\\nsvg {\\n  width: auto;\\n  height: 1.125rem;\\n}\\n\",\"/app/ce-language-switch.component.css\":\"/* Matches the header's nav menu: muted entries, the current one in full color. */\\nmenu[popover] button[role=\\\"menuitem\\\"] {\\n  color: var(--muted-foreground);\\n\\n  &[aria-current=\\\"true\\\"] {\\n    color: var(--foreground);\\n    font-weight: 600;\\n  }\\n}\\n\",\"/features/recipes/ce-recipe-detail.component.css\":\"h1,\\nh2,\\nh3 {\\n  margin: 0;\\n}\\n\\nh1 {\\n  overflow-wrap: anywhere;\\n}\\n\\n.flag-mark {\\n  color: var(--warning);\\n}\\n\\n.meta a {\\n  color: inherit;\\n}\\n\\n.description {\\n  margin: 0;\\n  white-space: pre-line;\\n}\\n\\n.top {\\n  display: grid;\\n  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);\\n  gap: var(--space-4);\\n  align-items: start;\\n\\n  &:not(:has(.thumbnail)) {\\n    grid-template-columns: minmax(0, 1fr);\\n  }\\n\\n  @media (max-width: 720px) {\\n    grid-template-columns: minmax(0, 1fr);\\n  }\\n}\\n\\n.thumbnail {\\n  display: block;\\n  width: 100%;\\n  max-height: 28rem;\\n  object-fit: cover;\\n  border-radius: var(--radius-medium);\\n}\\n\\n.your-rating,\\n.facts .card {\\n  display: flex;\\n  flex-direction: column;\\n  gap: var(--space-3);\\n}\\n\\n.facts {\\n  display: grid;\\n  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));\\n  gap: var(--space-4);\\n  align-items: start;\\n\\n  & ul {\\n    margin: 0;\\n    padding-inline-start: var(--space-5);\\n  }\\n\\n  & p {\\n    margin: 0;\\n  }\\n}\\n\\n.step {\\n  white-space: pre-line;\\n}\\n\\n.review {\\n  margin: 0;\\n\\n  & p {\\n    margin: 0;\\n    white-space: pre-line;\\n  }\\n}\\n\",\"/features/recipes/ce-recipe-import.component.css\":\"h1 {\\n  margin: 0;\\n}\\n\\ntextarea {\\n  margin-block-start: var(--space-1);\\n  font-family: var(--font-mono);\\n}\\n\\ntd.url {\\n  overflow-wrap: anywhere;\\n}\\n\\n.succeeded {\\n  color: var(--success);\\n}\\n\\n.failed {\\n  color: var(--danger);\\n}\\n\",\"/features/recipes/ce-recipe-list.component.css\":\".filters {\\n  display: flex;\\n  flex-wrap: wrap;\\n  gap: var(--space-2);\\n\\n  & .search {\\n    flex: 2 1 16rem;\\n    margin: 0;\\n  }\\n\\n  & .sort {\\n    flex: 1 1 14rem;\\n    flex-wrap: nowrap;\\n\\n    & select {\\n      flex: 1 1 auto;\\n      min-width: 0;\\n    }\\n  }\\n}\\n\\n.grid-cards {\\n  display: grid;\\n  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));\\n  gap: var(--space-4);\\n}\\n\\nce-pagination {\\n  justify-self: center;\\n}\\n\",\"/features/recipes/ce-recipe-edit.component.css\":\"h1,\\nh2 {\\n  margin: 0;\\n}\\n\\nh1 {\\n  overflow-wrap: anywhere;\\n}\\n\\n.columns {\\n  display: grid;\\n  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));\\n  gap: var(--space-4);\\n  align-items: start;\\n}\\n\\n.field input,\\n.field textarea {\\n  margin-block-start: var(--space-1);\\n}\\n\\n.preview {\\n  display: block;\\n  width: 100%;\\n  aspect-ratio: 16 / 9;\\n  object-fit: cover;\\n  border-radius: var(--radius-medium);\\n  background-color: var(--muted);\\n}\\n\\not-upload {\\n  box-sizing: border-box;\\n  border-style: dashed;\\n}\\n\\n.nutrition {\\n  display: grid;\\n  grid-template-columns: repeat(6, minmax(0, 1fr));\\n  gap: var(--space-2);\\n\\n  @media (max-width: 1100px) {\\n    grid-template-columns: repeat(4, minmax(0, 1fr));\\n  }\\n\\n  @media (max-width: 720px) {\\n    grid-template-columns: repeat(3, minmax(0, 1fr));\\n  }\\n\\n  @media (max-width: 420px) {\\n    grid-template-columns: repeat(2, minmax(0, 1fr));\\n  }\\n\\n  & label {\\n    overflow: hidden;\\n    text-overflow: ellipsis;\\n    white-space: nowrap;\\n  }\\n}\\n\\n.actions {\\n  justify-content: flex-end;\\n}\\n\",\"/features/recipes/internal/ce-rating.component.css\":\":host {\\n  --star-size: 2rem;\\n\\n  display: inline-block;\\n}\\n\\n:host([readonly]) {\\n  --star-size: 1.125rem;\\n}\\n\\n/* Each label is half a star: a `half` shows its left half, a `full` its right. */\\n.stars {\\n  display: inline-flex;\\n  flex-direction: row-reverse;\\n\\n  & input {\\n    position: absolute;\\n    opacity: 0;\\n    width: 1px;\\n    height: 1px;\\n    margin: 0;\\n    pointer-events: none;\\n  }\\n\\n  & label {\\n    display: inline-block;\\n    width: calc(var(--star-size) / 2);\\n    height: var(--star-size);\\n    overflow: hidden;\\n    color: var(--muted);\\n    cursor: pointer;\\n    transition: color var(--transition-fast);\\n\\n    & svg {\\n      display: block;\\n      width: var(--star-size);\\n      max-width: none;\\n      height: var(--star-size);\\n      fill: currentColor;\\n    }\\n\\n    &.full svg {\\n      margin-inline-start: calc(var(--star-size) / -2);\\n    }\\n  }\\n\\n  & label.half {\\n    margin-inline-start: 2px;\\n  }\\n\\n  & input:checked ~ label {\\n    color: var(--warning);\\n  }\\n\\n  & input:focus-visible + label {\\n    outline: 2px solid var(--ring);\\n    outline-offset: 1px;\\n  }\\n\\n  &:not(.readonly) label:hover,\\n  &:not(.readonly) label:hover ~ label {\\n    color: var(--warning);\\n    opacity: 0.8;\\n  }\\n\\n  &.readonly label {\\n    cursor: default;\\n  }\\n}\\n\",\"/features/recipes/internal/ce-recipe-card.component.css\":\":host {\\n  height: 100%;\\n}\\n\\narticle.open-card {\\n  height: 100%;\\n  box-sizing: border-box;\\n  overflow: hidden;\\n\\n  & .thumb {\\n    display: block;\\n    width: calc(100% + 2 * var(--space-6));\\n    max-width: none;\\n    aspect-ratio: 16 / 9;\\n    margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-6)) var(--space-2);\\n    object-fit: cover;\\n    background-color: var(--muted);\\n\\n    @media (max-width: 640px) {\\n      width: calc(100% + 2 * var(--space-4));\\n      margin: calc(-1 * var(--space-4)) calc(-1 * var(--space-4)) var(--space-2);\\n    }\\n  }\\n\\n  & .head {\\n    align-items: flex-start;\\n  }\\n\\n  & .open {\\n    overflow-wrap: anywhere;\\n  }\\n\\n  & .flag {\\n    color: var(--warning);\\n    margin-inline-end: var(--space-1);\\n  }\\n\\n  & .description {\\n    display: -webkit-box;\\n    -webkit-box-orient: vertical;\\n    -webkit-line-clamp: 2;\\n    line-clamp: 2;\\n    overflow: hidden;\\n    margin: 0;\\n  }\\n\\n  & .foot {\\n    margin-top: auto;\\n  }\\n\\n  &.flagged {\\n    box-shadow: inset 0 0 0 2px var(--warning);\\n  }\\n\\n  & .actions svg {\\n    width: 1.125rem;\\n    height: 1.125rem;\\n  }\\n}\\n\",\"/features/recipes/internal/ce-tag-panel.component.css\":\"details {\\n  & .hstack {\\n    flex-wrap: nowrap;\\n\\n    & input {\\n      flex: 1 1 auto;\\n      width: auto;\\n      min-width: 0;\\n    }\\n\\n    & button {\\n      flex: none;\\n    }\\n  }\\n}\\n\\n.tags {\\n  display: flex;\\n  flex-wrap: wrap;\\n  gap: var(--space-2);\\n\\n  & button {\\n    gap: var(--space-2);\\n  }\\n\\n  & .count {\\n    font-variant-numeric: tabular-nums;\\n    opacity: 0.75;\\n  }\\n}\\n\",\"/features/recipes/internal/ce-ingredients.component.css\":\"header h2 {\\n  margin: 0;\\n}\\n\\n.portions {\\n  flex-wrap: nowrap;\\n\\n  & input {\\n    width: 4.5rem;\\n    text-align: center;\\n  }\\n}\\n\\ntd.amount {\\n  width: 1%;\\n  font-variant-numeric: tabular-nums;\\n  font-weight: 600;\\n}\\n\",\"/features/recipes/internal/ce-list-field.component.css\":\"fieldset {\\n  margin: 0;\\n  padding: 0;\\n  border: none;\\n}\\n\\nlegend {\\n  font-weight: 600;\\n  padding: 0;\\n}\\n\\n.items {\\n  margin: 0;\\n  padding: 0;\\n}\\n\\n.item {\\n  display: flex;\\n  align-items: flex-start;\\n  gap: var(--space-2);\\n\\n  & input,\\n  & textarea {\\n    flex: 1 1 auto;\\n    min-width: 0;\\n    margin: 0;\\n  }\\n\\n  & .buttons {\\n    flex: none;\\n    flex-wrap: nowrap;\\n  }\\n\\n  @media (max-width: 720px) {\\n    flex-wrap: wrap;\\n\\n    & .buttons {\\n      margin-inline-start: auto;\\n    }\\n  }\\n}\\n\",\"/features/recipes/internal/ce-history-field.component.css\":\"fieldset {\\n  margin: 0;\\n  padding: 0;\\n  border: none;\\n}\\n\\nlegend {\\n  font-weight: 600;\\n  padding: 0;\\n}\\n\\nol {\\n  margin: 0;\\n  padding: 0;\\n}\\n\\n.entry {\\n  display: flex;\\n  flex-wrap: wrap;\\n  align-items: center;\\n  gap: var(--space-2);\\n\\n  & input {\\n    flex: 1 1 9rem;\\n    width: auto;\\n    min-width: 0;\\n    margin: 0;\\n  }\\n\\n  & button {\\n    flex: none;\\n  }\\n}\\n\",\"/features/auth/ce-login.component.css\":\"section {\\n  max-inline-size: 24rem;\\n  margin-inline: auto;\\n}\\n\",\"/vendor/oat.css\":\"@layer theme,base,components,animations,utilities;@layer base{*,*:before,*:after{box-sizing:border-box;-webkit-tap-highlight-color:transparent}*{margin:0}html{tab-size:4}body,dialog,[popover]{font-family:var(--font-sans);font-size:var(--text-regular);line-height:var(--leading-normal);color:var(--foreground)}body{background-color:var(--background);color:var(--foreground);-webkit-font-smoothing:antialiased}main{padding-block-start:var(--space-8)}img,picture,video,canvas,svg{max-width:100%}p,h1,h2,h3,h4,h5,h6{overflow-wrap:break-word}h1,h2,h3,h4,h5,h6{font-weight:var(--font-semibold);line-height:1.25;&:first-child{margin-block-start:0}}h1{font-size:var(--text-1);margin:var(--space-10) 0 var(--space-6)}h2{font-size:var(--text-2);margin:var(--space-8) 0 var(--space-5)}h3{font-size:var(--text-3);margin:var(--space-6) 0 var(--space-4)}h4{font-size:var(--text-4);margin:var(--space-5) 0 var(--space-3)}h5{font-size:var(--text-5);margin:var(--space-4) 0 var(--space-2)}h6{font-size:var(--text-regular);margin:var(--space-4) 0 var(--space-2)}p{margin-block-end:var(--space-4);&:last-child{margin-block-end:0}}a{color:var(--primary);text-decoration:none;text-underline-offset:2px;transition:color var(--transition-fast);&:hover{color:rgb(from var(--primary) r g b / .8)}}:is(p,blockquote,figcaption) a{text-decoration:underline}strong,b{font-weight:var(--font-semibold)}em,i{font-style:italic}small{font-size:var(--text-7)}code{font-family:var(--font-mono);font-size:.875em;padding:calc(var(--space-1) / 2) var(--space-1);background-color:var(--muted);border-radius:var(--radius-small)}pre{font-family:var(--font-mono);padding:var(--space-4);background-color:var(--faint);border-radius:var(--radius-medium);overflow-x:auto;margin-block-end:var(--space-4);code{padding:0;background:none;border-radius:0}}blockquote{border-inline-start:2px solid var(--border);padding-inline-start:var(--space-4);margin:var(--space-4) 0;color:var(--muted-foreground);font-style:italic}hr{border:none;border-top:1px solid var(--border);margin:var(--space-8) 0}ul,ol{padding-inline-start:var(--space-6);margin-block-end:var(--space-4)}ul{list-style-type:disc}ol{list-style-type:decimal}li{margin-block-end:var(--space-1)}mark{background-color:rgb(from var(--warning) r g b / .3);padding:calc(var(--space-1) / 2) var(--space-1);border-radius:var(--radius-small)}[hidden]{display:none}:focus-visible{outline:2px solid var(--ring);outline-offset:2px}:disabled,[aria-disabled=true]{cursor:not-allowed;opacity:.5}}@layer theme{:root{color-scheme:light dark;--background: light-dark(#fff, #09090b);--foreground: light-dark(#262626, #e5e5e5);--card: light-dark(#fff, #18181b);--card-foreground: light-dark(#262626, #e5e5e5);--primary: light-dark(#262626, #e5e5e5);--primary-foreground: light-dark(#fafafa, #18181b);--secondary: light-dark(#f4f4f5, #27272a);--secondary-foreground: light-dark(#262626, #fafafa);--muted: light-dark(#f4f4f5, #27272a);--muted-foreground: light-dark(#71717a, #a1a1aa);--faint: light-dark(#fafafa, #1e1e21);--faint-foreground: light-dark(#a1a1aa, #71717a);--accent: light-dark(#f4f4f5, #27272a);--danger: light-dark(#d32f2f, #f4807b);--danger-foreground: light-dark(#fafafa, #18181b);--success: light-dark(#008032, #6cc070);--success-foreground: light-dark(#fafafa, #18181b);--warning: light-dark(#a65b00, #f0a030);--warning-foreground: #09090b;--border: light-dark(#e4e4e7, #303036);--input: light-dark(#e4e4e7, #3f3f46);--ring: light-dark(#71717a, #a1a1aa);--space-1: .25rem;--space-2: .5rem;--space-3: .75rem;--space-4: 1rem;--space-5: 1.25rem;--space-6: 1.5rem;--space-8: 2rem;--space-10: 2.5rem;--space-12: 3rem;--space-14: 3.5rem;--space-16: 4rem;--space-18: 4.5rem;--radius-small: .125rem;--radius-medium: .25rem;--radius-large: .5rem;--radius-full: 9999px;--bar-height: .5rem;--font-sans: system-ui, sans-serif;--font-mono: ui-monospace, Consolas, monospace;--text-1: clamp(1.75rem, 1.5rem + 1.1vw, 2.25rem);--text-2: clamp(1.5rem, 1.3rem + .8vw, 1.875rem);--text-3: clamp(1.25rem, 1.1rem + .5vw, 1.5rem);--text-4: clamp(1.125rem, 1.05rem + .3vw, 1.25rem);--text-5: 1.125rem;--text-6: 1rem;--text-7: .875rem;--text-8: .75rem;--text-regular: var(--text-6);--leading-normal: 1.5;--font-normal: 400;--font-medium: 500;--font-semibold: 600;--font-bold: 600;--shadow-small: 0 1px 2px rgb(0 0 0 / .06);--shadow-medium: 0 1px 2px rgb(0 0 0 / .08), 0 3px 6px -2px rgb(0 0 0 / .12);--shadow-large: 0 2px 4px -1px rgb(0 0 0 / .1), 0 10px 20px -8px rgb(0 0 0 / .22);--transition-fast: .12s cubic-bezier(.4, 0, .2, 1);--transition: .3s cubic-bezier(.32, .72, 0, 1);--z-dropdown: 50;--z-modal: 200}[data-variant=success]{--_variant-color: var(--success)}[data-variant=warning]{--_variant-color: var(--warning)}:is([data-variant=danger],[data-variant=error]){--_variant-color: var(--danger)}}@layer animations{@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}dialog::backdrop{opacity:1;transition:opacity .15s cubic-bezier(.4,0,.2,1);@starting-style{opacity:0}}@keyframes fade-in-up{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}}@layer components{figure[data-variant=avatar]:not([role=group]){display:inline-flex;align-items:center;justify-content:center;width:var(--sz, 2.5rem);height:var(--sz, 2.5rem);color:var(--primary);background-color:var(--muted);border-radius:var(--radius-full);font-weight:var(--font-medium);overflow:hidden;>img{width:100%;height:100%;object-fit:cover}&.small{--sz: 2rem}&.large{--sz: 3.25rem}}figure[data-variant=avatar][role=group]{display:inline-flex;align-items:center;margin:0;& figure[data-variant=avatar]{isolation:isolate;margin-inline-end:calc(var(--space-5) * -1);border:2px solid var(--background);&:last-child{margin-inline-end:0}}&.small{--sz: 2rem;& figure[data-variant=avatar]{margin-inline-end:calc(var(--space-4) * -.8);border-width:1px}}&.large{--sz: 3.25rem;& figure[data-variant=avatar]{margin-inline-end:calc(var(--space-6) * -1)}}}}@layer base{:is(button,[type=submit],[type=reset],[type=button],a.button),::file-selector-button{--_hov: color-mix(in srgb, var(--primary), var(--background) 20%);display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);padding:var(--space-2) var(--space-4);font-family:inherit;font-size:var(--text-7);font-weight:var(--font-medium);line-height:var(--leading-normal);white-space:nowrap;text-decoration:none;background-color:var(--primary);color:var(--primary-foreground);border-radius:var(--radius-medium);border:1px solid;border-color:rgb(255 255 255 / .12) rgb(0 0 0 / .08) rgb(0 0 0 / .16);transition:background-color var(--transition-fast),border-color var(--transition-fast),color var(--transition-fast),opacity var(--transition-fast),transform var(--transition-fast);&:not(:disabled){cursor:pointer}&:hover:not(:disabled){background-color:var(--_hov)}&:active:not(:disabled){transform:translate(1px,1px)}&[data-variant=secondary]{--_hov: color-mix(in srgb, var(--secondary), var(--secondary-foreground) 6%);background-color:var(--secondary);color:var(--secondary-foreground);border-color:color-mix(in srgb,var(--secondary),var(--secondary-foreground) 7%);border-right-color:#0000001a;border-bottom-color:#0000001a}&[data-variant=danger]{--_hov: color-mix(in srgb, var(--danger), black 15%);background-color:var(--danger);color:var(--danger-foreground)}&:is(.outline,.ghost){--_hov: var(--accent);background-color:transparent;color:var(--foreground);&[data-variant=danger]{--_hov: color-mix(in srgb, var(--danger), transparent 90%);color:var(--danger)}&[data-variant=secondary]{--_hov: color-mix(in srgb, var(--secondary), transparent 80%);color:var(--secondary-foreground)}}&.outline{border-color:var(--border);&[data-variant=danger]{border-color:var(--danger)}&[data-variant=secondary]{border-color:var(--secondary)}}&.ghost{border-color:transparent}&.small{padding:var(--space-1) var(--space-3);font-size:var(--text-8)}&.large{height:3rem;padding:0 var(--space-6);font-size:var(--text-regular)}&.icon{width:2.5rem;padding:0;&.small{width:2rem}&.large{width:3rem}}}::file-selector-button{background-color:transparent;color:var(--foreground);border:1px solid var(--border)}::file-selector-button:hover{background-color:var(--accent)}}@layer components{menu.buttons{list-style-type:none;padding-inline-start:0;display:inline-flex;>li{&:first-child>*{border-start-start-radius:var(--radius-medium);border-end-start-radius:var(--radius-medium)}&:last-child>*{border-start-end-radius:var(--radius-medium);border-end-end-radius:var(--radius-medium)}>*{border-radius:0}&:not(:last-child)>*{border-inline-end:1px solid rgb(from var(--primary-foreground) r g b / .2)}}}}@layer base{label{display:block;font-size:var(--text-7);font-weight:var(--font-medium);&:has(input:where([type=checkbox],[type=radio])){display:inline-flex;align-items:center;gap:var(--space-2);row-gap:0;font-weight:var(--font-normal)}}:where(input:not([type=checkbox],[type=radio],[type=range],[type=file],[type=color],[type=submit],[type=reset],[type=button]),textarea,select){font-family:var(--font-sans);width:100%;margin-block-start:var(--space-1);padding:var(--space-2) var(--space-3);font-size:var(--text-7);line-height:var(--leading-normal);background-color:var(--background);color:var(--foreground);border:1px solid var(--input);border-radius:var(--radius-medium);transition:border-color var(--transition-fast),box-shadow var(--transition-fast);&::placeholder{color:var(--muted-foreground)}&:focus{outline:none;border-color:var(--ring);box-shadow:0 0 0 3px rgb(from var(--ring) r g b / .25);z-index:1}&:disabled{background-color:var(--muted)}&:is([aria-invalid=true],:user-invalid){border-color:var(--danger);&:focus{box-shadow:0 0 0 3px rgb(from var(--danger) r g b / .2)}}}textarea{height:auto;min-height:5rem;padding:var(--space-3);resize:vertical}select{appearance:none;background-image:url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\\\");background-repeat:no-repeat;background-position:right var(--space-2) center;padding-inline-end:var(--space-6)}input:where([type=checkbox],[type=radio]){appearance:none;width:1rem;height:1rem;margin:0;position:relative;background-color:var(--background);border:1px solid var(--input);transition:background-color var(--transition),border-color var(--transition),box-shadow var(--transition-fast);&:checked{background-color:var(--primary);border-color:var(--primary);&:after{content:\\\"\\\";position:absolute;inset:0;background-color:var(--primary-foreground);mask-position:center;mask-repeat:no-repeat;mask-size:100%}}}input[type=checkbox]{border-radius:var(--radius-small);&:checked:after{mask-image:url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='4'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E\\\")}&[role=switch]{--switch-height: calc(var(--bar-height) * 3);--switch-inset: 2px;--switch-thumb: calc(var(--switch-height) - var(--switch-inset) * 3);width:calc(var(--switch-height) * 2);height:var(--switch-height);border-radius:var(--radius-full);background-color:var(--input);&:before{content:\\\"\\\";position:absolute;top:50%;left:var(--switch-inset);transform:translateY(-50%);width:var(--switch-thumb);height:var(--switch-thumb);background-color:var(--background);border-radius:var(--radius-full);transition:transform .35s cubic-bezier(.34,1.56,.64,1);box-shadow:var(--shadow-small)}&:checked{background-color:var(--primary);&:after{content:none}&:before{transform:translateY(-50%) translate(var(--switch-height))}}}}input[type=radio]{border-radius:var(--radius-full);&:checked:after{mask-image:url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='4' fill='currentColor'/%3E%3C/svg%3E\\\")}}:where(input:where([type=checkbox],[type=radio],[type=range]),select):not(:disabled),label:has(input:where([type=checkbox],[type=radio]):not(:disabled)){cursor:pointer}input[type=range]{width:100%;height:var(--bar-height);appearance:none;background:var(--muted);border-radius:var(--radius-full);&::-webkit-slider-thumb{appearance:none;width:1.25rem;height:1.25rem;background:var(--primary);border-radius:var(--radius-full);transition:transform var(--transition-fast);&:hover{transform:scale(1.15)}}&::-moz-range-thumb{width:1.25rem;height:1.25rem;background:var(--primary);border:none;border-radius:var(--radius-full)}}fieldset{border:1px solid var(--border);border-radius:var(--radius-medium);padding:var(--space-4);margin-block-end:var(--space-4)}legend{font-size:var(--text-7);font-weight:var(--font-medium);padding:0 var(--space-2)}}@layer components{fieldset.group{display:flex;align-items:stretch;border:none;padding:0;>:is(input:not([type=submit],[type=reset],[type=button]),textarea,select){flex:1;margin-block-start:0;&:not(:focus):not(:last-child){border-inline-end-color:transparent}}>:is(input,textarea,select,button){border-radius:0;&:first-child{border-radius:var(--radius-medium) 0 0 var(--radius-medium)}&:last-child{border-radius:0 var(--radius-medium) var(--radius-medium) 0}}>legend,>label{float:inline-start;display:inline-flex;align-items:center;padding:0 var(--space-3);line-height:var(--leading-normal);font-weight:var(--font-normal);color:var(--muted-foreground);background-color:var(--muted);border:1px solid var(--input);&:not(:last-child){border-inline-end:0}&:first-child{border-radius:var(--radius-medium) 0 0 var(--radius-medium)}&:last-child{border-radius:0 var(--radius-medium) var(--radius-medium) 0}}}[data-field]{margin-block-end:var(--space-4);flex-wrap:wrap;[data-hint],.error{font-size:var(--text-8);font-weight:var(--font-normal);color:var(--muted-foreground);margin-block-start:var(--space-1);display:block;flex-basis:100%;min-inline-size:100%}.error{display:none}&:is([aria-invalid=true],:has([aria-invalid=true])) .error{display:block;color:var(--danger)}}}@layer base{.table{min-width:320px;width:100%;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--text-7)}thead{border-bottom:1px solid var(--border)}tfoot{border-top:1px solid var(--border)}th,td{overflow-wrap:break-word}th{padding:var(--space-3) var(--space-2);text-align:start;font-weight:var(--font-medium);color:var(--muted-foreground)}td{padding:var(--space-3) var(--space-2)}tbody tr{border-bottom:1px solid var(--border);transition:background-color var(--transition-fast);&:last-child{border-bottom:none}&:hover{background-color:rgb(from var(--muted) r g b / .5)}}}@layer base{progress,meter{appearance:none;width:100%;height:var(--bar-height);border:none;border-radius:var(--radius-full);overflow:hidden;background:var(--muted)}progress{&::-webkit-progress-bar{background-color:var(--muted);border-radius:var(--radius-full)}&::-webkit-progress-value{background-color:var(--primary);border-radius:var(--radius-full);transition:width var(--transition)}&::-moz-progress-bar{background-color:var(--primary);border-radius:var(--radius-full)}}meter{&::-webkit-meter-bar{background:var(--muted);border:none;border-radius:var(--radius-full);height:var(--bar-height)}&::-webkit-meter-optimum-value,&::-webkit-meter-suboptimum-value,&::-webkit-meter-even-less-good-value{border-radius:var(--radius-full)}&::-webkit-meter-optimum-value{background:var(--success)}&::-webkit-meter-suboptimum-value{background:var(--warning)}&::-webkit-meter-even-less-good-value{background:var(--danger)}&::-moz-meter-bar{background:var(--success);border-radius:var(--radius-full)}&:-moz-meter-sub-optimum::-moz-meter-bar{background:var(--warning)}&:-moz-meter-sub-sub-optimum::-moz-meter-bar{background:var(--danger)}}}@layer components{[aria-busy=true]{&:before{content:\\\"\\\";display:inline-block;width:1.5rem;height:1.5rem;border:2px solid var(--muted);border-top-color:var(--primary);border-radius:var(--radius-full);animation:spin 1s linear infinite}&[data-spinner~=small]:before{width:1rem;height:1rem}&[data-spinner~=large]:before{width:2rem;height:2rem;border-width:3px}&[data-spinner~=overlay]{position:relative;>*{opacity:.3;pointer-events:none}&:before{position:absolute;inset:0;margin:auto;z-index:1}}}@keyframes spin{to{transform:rotate(360deg)}}}@layer components{:root{--grid-cols: 12;--grid-gap: 1.5rem;--container-max: 1280px;--container-pad: 1rem}.container{width:100%;max-width:var(--container-max);margin-inline:auto;padding-inline:var(--container-pad)}.row{display:grid;grid-template-columns:repeat(var(--grid-cols),1fr);gap:var(--grid-gap);width:100%}.col,[class*=col-]{grid-column-end:span calc(var(--span, var(--grid-cols)) + var(--offset, 0))}.col-1{--span: 1}.col-2{--span: 2}.col-3{--span: 3}.col-4{--span: 4}.col-5{--span: 5}.col-6{--span: 6}.col-7{--span: 7}.col-8{--span: 8}.col-9{--span: 9}.col-10{--span: 10}.col-11{--span: 11}.col-12{--span: 12}.offset-1{--offset: 1}.offset-2{--offset: 2}.offset-3{--offset: 3}.offset-4{--offset: 4}.offset-5{--offset: 5}.offset-6{--offset: 6}[class*=offset-]{margin-inline-start:calc(var(--offset) * (100% + var(--grid-gap)) / (var(--span) + var(--offset)))}.col-end{grid-column-start:span var(--span, 1);grid-column-end:-1}@media(max-width:768px){.row{--grid-cols: 4;--grid-gap: 1rem}.col,[class*=col-]{--span: 4}[class*=offset-]{--offset: 0;margin-inline-start:0}}}@layer components{.card{background-color:var(--card);color:var(--card-foreground);border:1px solid var(--border);border-radius:var(--radius-medium);box-shadow:var(--shadow-small);padding:var(--space-6)}}@layer components{[role=alert]{position:relative;display:flex;gap:var(--space-3);padding:var(--space-4) var(--space-6);background-color:var(--background);border:1px solid var(--border);border-radius:var(--radius-medium);font-size:var(--text-7);border-left-width:var(--space-2);border-left-color:var(--border);&[data-variant]{border-width:0 0 0 var(--space-2)}&:is([data-variant=error],[data-variant=danger],[data-variant=success],[data-variant=warning]){color:var(--_variant-color);border-left-color:var(--_variant-color);background-color:light-dark(color-mix(in srgb,var(--_variant-color) 8%,transparent),color-mix(in srgb,var(--_variant-color) 20%,transparent));& a{color:var(--_variant-color)}}}}@layer components{.badge{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-1);padding:calc(var(--space-1) / 2) var(--space-3);font-size:var(--text-8);font-weight:var(--font-medium);text-wrap:nowrap;line-height:var(--leading-normal);background-color:var(--primary);color:var(--primary-foreground);border-radius:var(--radius-full);&.outline{background-color:transparent;color:var(--foreground);border:1px solid var(--border)}&[data-variant=secondary]{background-color:var(--secondary);color:var(--secondary-foreground)}&:is([data-variant=success],[data-variant=warning],[data-variant=danger]){color:var(--_variant-color);background-color:light-dark(color-mix(in srgb,var(--_variant-color) 10%,transparent),color-mix(in srgb,var(--_variant-color) 30%,transparent))}position:relative;button{all:unset;position:absolute;right:0;padding-inline:var(--space-3) var(--space-2);cursor:pointer;opacity:0;pointer-events:none;border-start-end-radius:inherit;border-end-end-radius:inherit;background-color:color-mix(in srgb,var(--muted) 60%,transparent);transition:opacity var(--transition-fast)}&:hover button,button:focus-visible{opacity:1;pointer-events:auto}}a.badge:hover,a:hover .badge{opacity:.8}}@layer components{details{border:1px solid var(--border);border-radius:var(--radius-medium);overflow:hidden;+details{margin-top:-1px;border-start-start-radius:0;border-start-end-radius:0}&:has(+details){border-end-start-radius:0;border-end-end-radius:0}&[open] summary{border-bottom:1px solid var(--border)}}summary{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--space-4);font-weight:var(--font-medium);cursor:pointer;user-select:none;transition:background-color var(--transition-fast);&:hover{background-color:var(--muted)}&::-webkit-details-marker,&::marker{display:none}&:after{content:\\\"\\\";width:1em;height:1em;flex-shrink:0;background-color:currentColor;mask-image:url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\\\");mask-size:contain;mask-repeat:no-repeat;transition:transform var(--transition)}details[open] &:after{transform:rotate(180deg)}}details>*:not(summary){margin:var(--space-4)}details[open]>*:not(summary){animation:fade-in-up .4s cubic-bezier(.32,.72,0,1) both}}@layer components{[role=tablist]{display:inline-flex;align-items:center;gap:var(--space-1);padding:var(--space-1);background-color:var(--muted);border-radius:var(--radius-medium)}[role=tab]{display:inline-flex;align-items:center;justify-content:center;padding:var(--space-2) var(--space-3);font-size:var(--text-7);font-weight:var(--font-medium);white-space:nowrap;background-color:transparent;color:var(--foreground);border:none;border-radius:calc(var(--radius-medium) - 2px);cursor:pointer;transition:background-color var(--transition),color var(--transition-fast),box-shadow var(--transition);&:hover{color:var(--muted-foreground)}&[aria-selected=true]{background-color:var(--background);box-shadow:var(--shadow-small)}}[role=tabpanel]{padding:var(--space-4) 0;&:focus-visible{outline:none}&:not([hidden]){animation:fade-in-up .4s cubic-bezier(.32,.72,0,1) both}}}@layer components{dialog{position:fixed;inset:0;z-index:var(--z-modal);width:min(100% - 2rem,32rem);max-height:85vh;margin:auto;padding:0;background-color:var(--card);border:1px solid var(--border);border-radius:var(--radius-large);box-shadow:var(--shadow-large);opacity:0;transform:translateY(.5rem) scale(.96);transition:opacity .2s ease,transform .3s cubic-bezier(.32,.72,0,1),overlay .3s ease allow-discrete,display .3s ease allow-discrete;&:is([open],:popover-open){opacity:1;transform:translateY(0) scale(1)}@starting-style{&:is([open],:popover-open){opacity:0;transform:translateY(.5rem) scale(.96)}}&::backdrop{background-color:#0000;transition:background-color .3s ease,overlay .3s ease allow-discrete,display .3s ease allow-discrete}&:is([open],:popover-open)::backdrop{background-color:light-dark(rgb(0 0 0 / .4),rgb(0 0 0 / .8))}@starting-style{&:is([open],:popover-open)::backdrop{background-color:#0000}}>header,>form>header{display:flex;flex-direction:column;gap:var(--space-1);padding:var(--space-6);padding-block-end:0;>:is(h1,h2,h3,h4,h5,h6){margin-block-end:0}>p{font-size:var(--text-7);color:var(--muted-foreground);margin-block-end:0}}:is(&,&>form)>:is(p,div,section){padding:var(--space-6);overflow-y:auto}>footer,>form>footer{display:flex;justify-content:flex-end;gap:var(--space-2);padding:var(--space-6);padding-block-start:0}}}@layer components{ot-dropdown{[popover]:not(.ot-tooltip){position:fixed;margin:0;min-width:12rem;background-color:var(--card);border:1px solid var(--border);border-radius:var(--radius-medium);box-shadow:var(--shadow-medium);opacity:0;transform:translateY(-4px) scale(.97);transition:opacity .15s ease-out,transform .25s cubic-bezier(.32,.72,0,1),display .25s allow-discrete,overlay .25s allow-discrete;&:popover-open{opacity:1;transform:translateY(0) scale(1)}@starting-style{&:popover-open{opacity:0;transform:translateY(-4px) scale(.97)}}hr{margin:var(--space-1) 0}}[role=menuitem]{display:flex;width:100%;padding:var(--space-2) var(--space-3);justify-content:start;cursor:pointer;&:hover,&:focus{background-color:var(--accent);outline:none}}}}@layer components{.toast-container{--_stack-direction: 1;position:fixed;display:flex;flex-direction:column-reverse;margin:0;padding:0;border:none;background:transparent;overflow:visible;&::backdrop{display:none}&[data-placement^=top]{inset:var(--space-4) auto auto auto}&[data-placement^=bottom]{--_stack-direction: -1;inset:auto auto var(--space-4) auto;flex-direction:column}&[data-placement$=left]{left:var(--space-4)}&[data-placement$=center]{left:50%;transform:translate(-50%)}&[data-placement$=right]{right:var(--space-4)}&:not(:hover):not(:focus-within){display:grid;>.toast{--_depth: 0;grid-area:1 / 1;z-index:0;translate:0 calc(var(--space-2) * var(--_depth) * var(--_stack-direction));scale:calc(1 - var(--_depth) * .03) 1;&:nth-last-child(2){--_depth: 1}&:nth-last-child(3){--_depth: 2}&:nth-last-child(4){--_depth: 3}&:nth-last-child(5){--_depth: 4}&:not(:last-child){pointer-events:none}&:nth-last-child(n+6),&:not(:last-child)>*{visibility:hidden}}}}.toast{--transition: .4s;--transition-in: calc(var(--transition) - 50ms);padding:var(--space-5) var(--space-4);max-width:28rem;min-width:20rem;pointer-events:auto;background-color:var(--card);border:1px solid var(--border);border-inline-start-width:var(--space-1);border-inline-start-style:solid;border-inline-start-color:var(--_variant-color, var(--border));border-radius:var(--radius-medium);box-shadow:var(--shadow-medium);transition:opacity var(--transition-in),transform var(--transition-in) cubic-bezier(.32,.72,0,1),translate var(--transition) cubic-bezier(.32,.72,0,1),scale var(--transition) cubic-bezier(.32,.72,0,1);line-height:1;.toast-title{font-weight:600;margin:0 0 var(--space-3) 0;color:var(--_variant-color, inherit)}.toast-message{color:var(--muted-foreground)}>[data-close]{margin-inline-start:auto;background:none;border:none;padding:0;cursor:pointer;opacity:.5;&:hover{opacity:1}}margin:var(--space-2) 0;&[data-entering]{opacity:0;transform:translateY(-1rem) scale(.96)}&[data-exiting]{opacity:0;transition:opacity var(--transition)}}}@layer components{:root{--sidebar-width: 15rem}[data-sidebar-layout]{display:grid;grid-template-columns:var(--sidebar-width) 1fr;grid-template-rows:auto 1fr;height:100dvh;>main{grid-row:2;min-width:0;overflow-y:auto}>aside[data-sidebar]{grid-row:2;min-height:0;z-index:1;background-color:var(--background);border-inline-end:1px solid var(--border);box-shadow:none;display:flex;flex-direction:column;>:is(header,footer){flex-shrink:0;padding:var(--space-3)}>footer{margin-block-start:auto}>nav{flex:1;min-height:0;overflow-y:auto;padding:var(--space-3) var(--space-2);font-size:var(--text-7);ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-1);li{margin:0}}a{display:flex;gap:var(--space-2);padding:var(--space-1) var(--space-3);color:var(--foreground);border-radius:var(--radius-medium);transition:background-color var(--transition-fast),color var(--transition-fast);&:is(:hover,[aria-current]){background-color:var(--accent)}}details{border:none;overflow:visible;+details{margin-top:0}&[open] summary{border-bottom:none}>ul{margin-inline-start:var(--space-4);padding:var(--space-1) 0}}summary{justify-content:flex-start;padding:var(--space-2) var(--space-3);border-radius:var(--radius-small);&:after{width:.75rem;height:.75rem;margin-inline-start:auto}}}}>:is(nav,header)[data-topnav]{grid-column:1 / -1}}:is(nav,header)[data-topnav]{position:sticky;top:0;z-index:5;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-4);background-color:var(--background);border-bottom:1px solid var(--border);box-shadow:none}:is([data-sidebar-toggle],[data-sidebar-header]){display:none}[data-sidebar-toggle]{padding:0 var(--space-1);background:none;border:1px solid var(--border);border-radius:var(--radius-small)}@media(min-width:769px){[data-sidebar-layout=always]{transition:grid-template-columns var(--transition);[data-sidebar-toggle]{display:inline-block}>aside[data-sidebar]{transform:translate(0);opacity:1;transition:transform var(--transition),opacity var(--transition),visibility var(--transition)}&[data-sidebar-open]{grid-template-columns:0px 1fr;gap:0;>aside[data-sidebar]{overflow:hidden;min-width:0;transform:translate(-100%);opacity:0;visibility:hidden;border-inline-end:none}}}}@media(max-width:768px){[data-sidebar-layout]{grid-template-columns:1fr;>main{grid-column:1}>aside[data-sidebar]{grid-column:1;z-index:2;width:min(var(--sidebar-width),100vw);transform:translate(calc(-100% - 5rem));transition:transform var(--transition);box-shadow:var(--shadow-large)}&[data-sidebar-open]>aside[data-sidebar]{transform:translate(0)}}[data-sidebar-toggle]{display:inline-block}[data-sidebar-header]{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);border-bottom:1px solid var(--border)}}}@layer components{ot-taginput{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);background-color:var(--background);border:1px solid var(--input);border-radius:var(--radius-medium);cursor:text;transition:border-color var(--transition-fast),box-shadow var(--transition-fast);width:100%;min-height:calc(1lh + var(--space-5));&:focus-within{border-color:var(--ring);box-shadow:0 0 0 2px rgb(from var(--ring) r g b / .2)}&[aria-disabled=true]{cursor:not-allowed;.badge button{opacity:0;pointer-events:none}}input{flex:1;min-width:8ch;margin:0;padding:0;border:none;background:none;&:focus{box-shadow:none}}}}@layer components{[role=status].skeleton{--_c: light-dark( color-mix(in srgb, var(--muted) 15%, white), color-mix(in srgb, var(--muted) 90%, var(--foreground)) );margin-block-end:var(--space-3);background:var(--muted);border-radius:var(--radius-medium);animation:anim 2s infinite;background-size:200% 100%;background-image:linear-gradient(90deg,var(--muted) 0%,var(--_c) 50%,var(--muted) 100%);&.box{width:4rem;height:4rem}&.line{height:1rem;width:100%}}[role=status].skeleton:last-child{margin-block-end:0}@keyframes anim{0%{background-position:200% 0}to{background-position:-200% 0}}}@layer components{.ot-tooltip{--_x: 0;--_y: -1;&[data-tooltip-placement=bottom]{--_y: 1}&[data-tooltip-placement=left]{--_x: -1;--_y: 0}&[data-tooltip-placement=right]{--_x: 1;--_y: 0}position:fixed;inset:auto;margin:0;padding:0;border:0;max-width:none;max-height:none;overflow:visible;background:none;pointer-events:none;animation:ot-tooltip var(--transition-fast) .7s both;&:before,&:after{content:\\\"\\\";position:absolute;left:calc((var(--_x) + 1) * 50%);top:calc((var(--_y) + 1) * 50%);translate:-50% -50%;background:var(--foreground)}&:before{width:11px;height:11px;transform:translate(calc(var(--_x) * 10px),calc(var(--_y) * 10px)) rotate(45deg)}&:after{content:attr(data-tooltip);transform:translate(calc(var(--_x) * (50% + 10px)),calc(var(--_y) * (50% + 10px)));padding:var(--space-2) var(--space-3);font-size:var(--text-7);line-height:1;white-space:nowrap;color:var(--background);border-radius:var(--radius-medium)}}@keyframes ot-tooltip{0%{opacity:0;transform:translate(calc(var(--_x) * -4px),calc(var(--_y) * -4px))}}}@layer components{ot-upload{cursor:pointer;width:100%;&[data-drag]{border-color:var(--ring);box-shadow:0 0 0 2px rgb(from var(--ring) r g b / .2)}.badge{text-overflow:ellipsis;max-width:20ch;overflow:hidden;display:inline-block}}}@layer utilities{.align-left{text-align:start}.align-center{text-align:center}.align-right{text-align:end}.text-light{color:var(--muted-foreground)}.text-lighter{color:var(--faint-foreground)}.flex{display:flex}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-end{justify-content:flex-end}.hstack{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;align-content:flex-start;height:auto;>*{margin:0}}.vstack{display:flex;flex-direction:column;gap:var(--space-3)}.gap-1{gap:var(--space-1)}.gap-2{gap:var(--space-2)}.gap-4{gap:var(--space-4)}.gap-6{gap:var(--space-6)}.mt-2{margin-block-start:var(--space-2)}.mt-4{margin-block-start:var(--space-4)}.mt-6{margin-block-start:var(--space-6)}.mt-8{margin-block-start:var(--space-8)}.mb-2{margin-block-end:var(--space-2)}.mb-4{margin-block-end:var(--space-4)}.mb-6{margin-block-end:var(--space-6)}.mb-8{margin-block-end:var(--space-8)}.p-4{padding:var(--space-4)}.w-100{width:100%}:is(ul,ol).unstyled{list-style:none;padding:0}a.unstyled{color:inherit;text-decoration:none;&:hover{color:var(--primary)}}}\\n\"};",
    "import { INLINE_STYLES } from './inline-styles.ts';\n\nconst BASE_HREFS = ['/vendor/oat.css', '/ui/shared.css'];\n\nconst sheets = new Map<string, CSSStyleSheet>();\n\nconst hrefs = new Map<string, string>();\n\nconst pending = new Map<string, Promise<void>>();\n\nlet importMap: Record<string, string> | undefined;\n\n// Not import.meta.resolve(): bun test resolves it against the file system.\nfunction versioned(href: string): string {\n  if (importMap === undefined) {\n    importMap = {};\n    const script = document.querySelector('script[type=\"importmap\"]');\n    try {\n      // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- written by the server's page.ts\n      importMap = (JSON.parse(script?.textContent ?? '{}') as { imports?: Record<string, string> }).imports ?? {};\n    } catch (cause) {\n      console.error('cena: could not read the import map', cause);\n    }\n  }\n  return importMap[href] ?? href;\n}\n\n// A refill revalidates: the versioned URL may be cached for good while the file changed on disk.\nasync function load(href: string, refill = false): Promise<void> {\n  const sheet = sheets.get(href) ?? new CSSStyleSheet();\n  sheets.set(href, sheet);\n  const inline = INLINE_STYLES[href];\n  if (inline !== undefined) {\n    await sheet.replace(inline);\n    return;\n  }\n  try {\n    const response = await fetch(versioned(href), refill ? { cache: 'no-cache' } : undefined);\n    if (!response.ok) {\n      // noinspection ExceptionCaughtLocallyJS\n      throw new Error(`HTTP ${response.status}`);\n    }\n\n    // replace() rather than replaceSync(): it tolerates @import instead of throwing.\n    await sheet.replace(await response.text());\n  } catch (cause) {\n    // An unstyled component is easier to diagnose than a blank page.\n    console.error(`cena: could not load stylesheet ${href}`, cause);\n    await sheet.replace('');\n  }\n}\n\nawait Promise.all(BASE_HREFS.map((href) => load(href)));\n\nexport function loadStyles(tagName: string, moduleUrl: string): Promise<void> {\n  // A pathname, like BASE_HREFS and the paths dev/hot.ts receives, so hot swaps find the sheet.\n  const href = new URL(moduleUrl, location.href).pathname.replace(/\\.ts$/, '.css');\n  hrefs.set(tagName, href);\n  let promise = pending.get(href);\n  if (!promise) {\n    promise = load(href);\n    pending.set(href, promise);\n  }\n  return promise;\n}\n\n// Synchronous for constructors; safe because define() awaits loadStyles before registering.\nexport function stylesFor(tagName: string): CSSStyleSheet[] {\n  const href = hrefs.get(tagName);\n  const own = href === undefined ? undefined : sheets.get(href);\n  const base = BASE_HREFS.flatMap((href) => {\n    const sheet = sheets.get(href);\n    return sheet ? [sheet] : [];\n  });\n  return own ? [...base, own] : base;\n}\n\nexport async function reloadSheet(href: string): Promise<boolean> {\n  if (!sheets.has(href)) {\n    return false;\n  }\n  await load(href, true);\n  return true;\n}\n",
    "import { raw } from './html.ts';\nimport type { RawHtml } from './html.ts';\nimport { onLanguageChange } from './i18n.ts';\nimport { loadStyles, stylesFor } from './styles.ts';\n\nexport class CeElement extends HTMLElement {\n  readonly root: ShadowRoot;\n  #stopLanguageSync: (() => void) | null = null;\n\n  constructor() {\n    super();\n    this.root = this.attachShadow({ mode: 'open' });\n    this.root.adoptedStyleSheets = stylesFor(this.localName);\n\n    this.root.addEventListener('click', (event) => {\n      const target = event.target instanceof Element ? event.target.closest('[data-action]') : null;\n      if (target instanceof HTMLElement && target.dataset.action) {\n        void this.handleAction(target.dataset.action, target, event);\n      }\n    });\n\n    this.root.addEventListener('submit', (event) => {\n      const form = event.target instanceof Element ? event.target.closest('form[data-action]') : null;\n      if (!(form instanceof HTMLFormElement) || !form.dataset.action) {\n        return;\n      }\n      event.preventDefault();\n      void this.handleSubmit(form.dataset.action, form, event);\n    });\n  }\n\n  connectedCallback(): void {\n    this.#stopLanguageSync ??= onLanguageChange(() => {\n      this.languageChanged();\n    });\n    this.render();\n  }\n\n  disconnectedCallback(): void {\n    this.#stopLanguageSync?.();\n    this.#stopLanguageSync = null;\n  }\n\n  // Every template reads its strings from t(); a component holding state the DOM must keep overrides this.\n  languageChanged(): void {\n    this.render();\n  }\n\n  template(): RawHtml {\n    return raw('');\n  }\n\n  render(): void {\n    this.root.innerHTML = String(this.template());\n    this.afterRender();\n  }\n\n  afterRender(): void {}\n\n  handleAction(_action: string, _element: HTMLElement, _event: Event): void | Promise<void> {}\n\n  handleSubmit(_action: string, _form: HTMLFormElement, _event: Event): void | Promise<void> {}\n\n  // Once-used type parameter on purpose, as querySelector's: callers know their own markup.\n  // oxlint-disable-next-line typescript/no-unnecessary-type-parameters\n  $<T extends Element = Element>(selector: string): T | null {\n    return this.root.querySelector<T>(selector);\n  }\n\n  $$<T extends Element = Element>(selector: string): T[] {\n    return Array.from(this.root.querySelectorAll<T>(selector));\n  }\n\n  emit(name: string, detail?: unknown): void {\n    this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));\n  }\n\n  formData(form: HTMLFormElement): Record<string, string> {\n    const values: Record<string, string> = {};\n    for (const [key, value] of new FormData(form).entries()) {\n      if (typeof value === 'string') {\n        values[key] = value.trim();\n      }\n    }\n    return values;\n  }\n}\n\nexport async function define(name: string, ctor: CustomElementConstructor, moduleUrl: string): Promise<void> {\n  if (customElements.get(name)) {\n    return;\n  }\n  await loadStyles(name, moduleUrl);\n  if (!customElements.get(name)) {\n    customElements.define(name, ctor);\n  }\n}\n",
    "export class ApiError extends Error {\n  // 0 when the request never left.\n  status: number;\n  details: unknown;\n\n  constructor(message: string, status: number, details: unknown) {\n    super(message);\n    this.name = 'ApiError';\n    this.status = status;\n    this.details = details;\n  }\n}\n\nexport function errorMessage(error: unknown): string {\n  return error instanceof Error ? error.message : String(error);\n}\n\n// An event, not a call: `http/` may not import the router in `app/`.\nexport const UNAUTHORIZED_EVENT = 'ce-unauthorized';\n",
    "import { ApiError, errorMessage } from '../http/errors.ts';\n\nexport type ToastKind = 'info' | 'success' | 'error';\n\ndeclare global {\n  interface Window {\n    // index.html loads oat.js, deferred, before any module runs.\n    ot: {\n      toast: (message: string, title?: string, options?: { variant?: string; placement?: string; duration?: number }) => HTMLElement;\n    };\n  }\n}\n\nexport function toast(message: string, kind: ToastKind = 'info'): void {\n  window.ot.toast(message, undefined, {\n    variant: kind === 'error' ? 'danger' : kind,\n    placement: 'bottom-right',\n    duration: kind === 'error' ? 6000 : 3000,\n  });\n}\n\nexport function toastError(error: unknown): void {\n  // ce-app sends the user to the login page instead.\n  if (error instanceof ApiError && error.status === 401) {\n    return;\n  }\n  toast(errorMessage(error), 'error');\n}\n",
    "import { ApiError, errorMessage } from '../http/errors.ts';\nimport { CeElement } from './base.ts';\nimport { html } from './html.ts';\nimport type { RawHtml } from './html.ts';\nimport { t } from './i18n.ts';\nimport { toastError } from './toast.ts';\n\nexport const PAGE_TITLE_EVENT = 'page-title';\n\nexport type ViewState<Data> = { status: 'loading' } | { status: 'ready'; data: Data } | { status: 'error'; message: string };\n\nexport abstract class CeView<Data> extends CeElement {\n  ready: Promise<void> = Promise.resolve();\n\n  #state: ViewState<Data> = { status: 'loading' };\n\n  get loadingText(): string {\n    return t().loading;\n  }\n\n  get data(): Data | undefined {\n    return this.#state.status === 'ready' ? this.#state.data : undefined;\n  }\n\n  titleFor(_data: Data): string | null {\n    return null;\n  }\n\n  get pageTitle(): string | null {\n    return this.#state.status === 'ready' ? this.titleFor(this.#state.data) : null;\n  }\n\n  /** Breadcrumb parents that depend on the data, such as the recipe above its edit page; null keeps the route's. */\n  parentsFor(_data: Data): { path: string; label: string }[] | null {\n    return null;\n  }\n\n  get pageParents(): { path: string; label: string }[] | null {\n    return this.#state.status === 'ready' ? this.parentsFor(this.#state.data) : null;\n  }\n\n  abstract load(): Promise<Data>;\n\n  abstract readyTemplate(data: Data): RawHtml;\n\n  errorTemplate(message: string): RawHtml {\n    return html`<p class=\"error-text\" data-testid=\"error\">${message}</p>`;\n  }\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.ready = this.reload();\n  }\n\n  // Never rejects: ce-app awaits `ready` before swapping views in.\n  async reload(): Promise<void> {\n    let data: Data;\n    try {\n      data = await this.load();\n    } catch (error) {\n      this.#state = { status: 'error', message: errorMessage(error) };\n      if (!(error instanceof ApiError) || error.status !== 404) {\n        toastError(error);\n      }\n      this.render();\n      this.emit(PAGE_TITLE_EVENT);\n      return;\n    }\n    this.show(data);\n  }\n\n  // For data a mutation's response already carries, so it needs no reload.\n  show(data: Data): void {\n    this.#state = { status: 'ready', data };\n    this.render();\n    this.emit(PAGE_TITLE_EVENT);\n  }\n\n  // For a change the view patches into its DOM itself, so the field being typed in is never replaced.\n  replaceData(data: Data): void {\n    this.#state = { status: 'ready', data };\n    this.emit(PAGE_TITLE_EVENT);\n  }\n\n  override template(): RawHtml {\n    if (this.#state.status === 'loading') {\n      return html`<p aria-busy=\"true\" data-testid=\"loading\">${this.loadingText}</p>`;\n    }\n    if (this.#state.status === 'error') {\n      return this.errorTemplate(this.#state.message);\n    }\n    return this.readyTemplate(this.#state.data);\n  }\n\n  // Routes match ids as `(\\d+)`, so this cannot produce NaN.\n  numericAttribute(name: string): number {\n    const value = this.getAttribute(name);\n    if (value === null) {\n      throw new Error(`${this.localName} needs a ${name} attribute`);\n    }\n    return Number(value);\n  }\n}\n",
    "import type { Language } from '../i18n/language.ts';\n\n// Labels read the current dictionary on every render, so they follow a language change.\nexport type Label = (language: Language) => string;\n\nexport interface Crumb {\n  path: string;\n  label: Label;\n}\n\nexport interface RouteDef {\n  pattern: RegExp;\n  keys: string[];\n  view: (params: Record<string, string>) => Promise<Element>;\n  title?: Label;\n  parents?: Crumb[];\n  // `path` is explicit because a regex cannot be turned back into an href.\n  nav?: Crumb;\n}\n\nexport interface RouteMatch {\n  route: RouteDef;\n  params: Record<string, string>;\n}\n\nexport function matchRoute(routes: readonly RouteDef[], path: string): RouteMatch | null {\n  for (const route of routes) {\n    const match = route.pattern.exec(path);\n    if (!match) {\n      continue;\n    }\n    const params: Record<string, string> = {};\n    route.keys.forEach((key, index) => {\n      params[key] = match[index + 1] ?? '';\n    });\n    return { route, params };\n  }\n  return null;\n}\n\nexport function currentPath(): string {\n  return location.pathname;\n}\n\nexport function navigate(path: string): void {\n  if (location.pathname + location.search !== path) {\n    history.pushState(null, '', path);\n  }\n  // pushState fires no event, and the same route must refresh too.\n  window.dispatchEvent(new PopStateEvent('popstate'));\n}\n\nexport function onRouteChange(listener: () => void): () => void {\n  window.addEventListener('popstate', listener);\n  return () => {\n    window.removeEventListener('popstate', listener);\n  };\n}\n\nexport function isActive(path: string): boolean {\n  const current = currentPath();\n  return path === '/' ? current === '/' : current.startsWith(path);\n}\n\nexport interface LinkClick {\n  button: number;\n  ctrlKey: boolean;\n  metaKey: boolean;\n  shiftKey: boolean;\n  altKey: boolean;\n  defaultPrevented: boolean;\n}\n\nexport interface LinkTarget {\n  href: string;\n  target: string;\n  download: boolean;\n}\n\nexport function linkPath(click: LinkClick, link: LinkTarget, origin: string): string | null {\n  const isHandledElsewhere = click.defaultPrevented;\n  const isPrimaryButton = click.button === 0;\n  const hasModifierKey = click.ctrlKey || click.metaKey || click.shiftKey || click.altKey;\n  const opensInOtherContext = link.target !== '' && link.target !== '_self';\n  const isDownload = link.download;\n  if (isHandledElsewhere || !isPrimaryButton || hasModifierKey || opensInOtherContext || isDownload) {\n    return null;\n  }\n\n  const url = new URL(link.href, origin);\n  const path = url.pathname;\n  const isOtherOrigin = url.origin !== origin;\n  const hasQueryOrFragment = url.search !== '' || url.hash !== '';\n  const isApiPath = path === '/api' || path.startsWith('/api/');\n  // The server's fallback serves index.html only for extension-less paths; anything else is a file.\n  const isFilePath = path.slice(path.lastIndexOf('/') + 1).includes('.');\n  if (isOtherOrigin || hasQueryOrFragment || isApiPath || isFilePath) {\n    return null;\n  }\n\n  return path;\n}\n",
    "import { ApiError, UNAUTHORIZED_EVENT } from './errors.ts';\n\n// A Blob goes as itself, typed by its own type; anything else as JSON.\nasync function request<T>(method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', path: string, body?: unknown): Promise<T> {\n  let response: Response;\n  const blob = body instanceof Blob;\n  try {\n    response = await fetch(`/api${path}`, {\n      method,\n      headers: body === undefined ? undefined : { 'Content-Type': blob ? body.type : 'application/json' },\n      // oxlint-disable-next-line unicorn/no-invalid-fetch-options\n      body: body === undefined ? undefined : blob ? body : JSON.stringify(body),\n    });\n  } catch (cause) {\n    throw new ApiError('Could not reach the cena server', 0, cause);\n  }\n\n  const text = await response.text();\n  let data: unknown = null;\n  try {\n    data = text === '' ? null : JSON.parse(text);\n  } catch {\n    data = null;\n  }\n\n  if (!response.ok) {\n    const errorBody = typeof data === 'object' && data !== null ? data : {};\n    const message = 'error' in errorBody && typeof errorBody.error === 'string' ? errorBody.error : `Request failed (${response.status})`;\n    if (response.status === 401) {\n      // globalThis is `window` in a browser; the tests of this module run without a DOM.\n      globalThis.dispatchEvent(new Event(UNAUTHORIZED_EVENT));\n    }\n    throw new ApiError(message, response.status, 'details' in errorBody ? errorBody.details : undefined);\n  }\n\n  // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the server contract boundary\n  return data as T;\n}\n\nexport const get = <T>(path: string): Promise<T> => request<T>('GET', path);\n\nexport const post = <T>(path: string, body?: unknown): Promise<T> => request<T>('POST', path, body ?? {});\n\nexport const put = <T>(path: string, body?: unknown): Promise<T> => request<T>('PUT', path, body ?? {});\n\nexport const patch = <T>(path: string, body?: unknown): Promise<T> => request<T>('PATCH', path, body ?? {});\n\nexport const remove = (path: string): Promise<null> => request<null>('DELETE', path);\n\nexport const putBlob = <T>(path: string, blob: Blob): Promise<T> => request<T>('PUT', path, blob);\n\n/** A DELETE whose answer carries a body, unlike `remove`. */\nexport const removeReturning = <T>(path: string): Promise<T> => request<T>('DELETE', path);\n",
    "import type { AuthStatusDto, LoginRequestDto } from '../../../../shared/dto/auth.ts';\nimport { post } from '../../../http/http.ts';\n\nexport class AuthApi {\n  /** Embedded by the server's `embedAuthStatus()`; without it, asking for a login is the safe guess. */\n  status(): AuthStatusDto {\n    const json = document.getElementById('auth-status')?.textContent ?? '';\n    // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the server contract boundary\n    return json === '' ? { enabled: true } : (JSON.parse(json) as AuthStatusDto);\n  }\n\n  async login(password: string): Promise<void> {\n    const body: LoginRequestDto = { password };\n    await post('/auth/login', body);\n  }\n\n  async logout(): Promise<void> {\n    await post('/auth/logout');\n  }\n}\n",
    "import { AuthApi } from './internal/auth.api.ts';\n\nexport class AuthFacade {\n  readonly #api: AuthApi;\n\n  constructor(api: AuthApi) {\n    this.#api = api;\n  }\n\n  enabled(): boolean {\n    return this.#api.status().enabled;\n  }\n\n  login(password: string): Promise<void> {\n    return this.#api.login(password);\n  }\n\n  logout(): Promise<void> {\n    return this.#api.logout();\n  }\n}\n\nexport const authFacade = new AuthFacade(new AuthApi());\n",
    "const LOGIN_PATH = '/login';\n\n// Resolved as a URL so `//evil`, `/\\evil` and tab-smuggled `/<TAB>/evil` are rejected.\nexport function nextPath(search: string, origin: string): string {\n  const next = new URLSearchParams(search).get('next');\n  if (next === null) {\n    return '/';\n  }\n  let url: URL;\n  try {\n    url = new URL(next, origin);\n  } catch {\n    return '/';\n  }\n  if (url.origin !== origin || url.pathname === LOGIN_PATH || url.pathname === `${LOGIN_PATH}/`) {\n    return '/';\n  }\n  return url.pathname + url.search;\n}\n",
    "import type { RawHtml } from '../../ui/html.ts';\nimport { define, CeElement } from '../../ui/base.ts';\nimport { html } from '../../ui/html.ts';\nimport { ApiError, errorMessage } from '../../http/errors.ts';\nimport { navigate } from '../../app/router.ts';\nimport { authFacade } from './auth.facade.ts';\nimport { t } from '../../ui/i18n.ts';\nimport { nextPath } from './internal/next-path.ts';\n\n// Light-DOM form, not a CeView: password managers skip shadow roots and forms added hidden.\nexport class CeLoginComponent extends CeElement {\n  override connectedCallback(): void {\n    super.connectedCallback();\n    if (!authFacade.enabled()) {\n      navigate(nextPath(location.search, location.origin));\n    }\n  }\n\n  async #login(form: HTMLFormElement): Promise<void> {\n    const button = form.querySelector('button[type=\"submit\"]');\n    const input = form.querySelector<HTMLInputElement>('input[name=\"password\"]');\n    button?.setAttribute('aria-busy', 'true');\n    try {\n      await authFacade.login(input?.value ?? '');\n      navigate(nextPath(location.search, location.origin));\n    } catch (error) {\n      this.#showError(error instanceof ApiError && error.status === 401 ? t().login.wrongPassword : errorMessage(error));\n      input?.select();\n    } finally {\n      button?.removeAttribute('aria-busy');\n    }\n  }\n\n  #showError(message: string): void {\n    const alert = this.querySelector<HTMLElement>('[role=\"alert\"]');\n    if (alert) {\n      alert.textContent = message;\n      alert.hidden = false;\n    }\n  }\n\n  override template(): RawHtml {\n    return html`\n      <section class=\"vstack gap-4\">\n        <h1>${t().login.title}</h1>\n        <slot></slot>\n      </section>\n    `;\n  }\n\n  // Own listener: happy-dom does not deliver slotted form events to the shadow root.\n  override afterRender(): void {\n    this.innerHTML = String(html`\n      <form class=\"vstack gap-2\" data-testid=\"form\">\n        <input type=\"text\" name=\"username\" autocomplete=\"username\" value=\"cena\" hidden />\n        <label>\n          ${t().login.password}\n          <input\n            type=\"password\"\n            name=\"password\"\n            placeholder=\"${t().login.placeholder}\"\n            autocomplete=\"current-password\"\n            required\n            autofocus\n            data-testid=\"password\"\n          />\n        </label>\n        <div role=\"alert\" data-variant=\"error\" data-testid=\"alert\" hidden></div>\n        <button type=\"submit\">${t().login.submit}</button>\n      </form>\n    `);\n    const form = this.querySelector('form');\n    form?.addEventListener('submit', (event) => {\n      event.preventDefault();\n      void this.#login(form);\n    });\n  }\n}\n\nawait define('ce-login', CeLoginComponent, \"/features/auth/ce-login.component.ts\");\n",
    "import { CeElement } from './base.ts';\n\nexport abstract class CeModal<Answer> extends CeElement {\n  #resolve: ((answer: Answer) => void) | null = null;\n\n  abstract answer(dialog: HTMLDialogElement): Answer;\n\n  // Appends the modal to the body and resolves its answer once the dialog closes, however it closes.\n  open(): Promise<Answer> {\n    const answer = new Promise<Answer>((resolve) => {\n      this.#resolve = resolve;\n    });\n    document.body.append(this);\n    return answer;\n  }\n\n  override afterRender(): void {\n    const dialog = this.$<HTMLDialogElement>('dialog');\n    if (!dialog) {\n      return;\n    }\n    // Oat's touch shim listens on `document`, where this shadow root's clicks name the host instead.\n    dialog.addEventListener('click', (event) => {\n      if (event.target === dialog) {\n        dialog.close();\n      }\n    });\n    dialog.addEventListener('close', () => {\n      this.#resolve?.(this.answer(dialog));\n      this.remove();\n    });\n    dialog.showModal();\n  }\n}\n",
    "import type { RawHtml } from '../html.ts';\nimport { define } from '../base.ts';\nimport { html } from '../html.ts';\nimport { t } from '../i18n.ts';\nimport { CeModal } from '../modal.ts';\n\nexport interface ConfirmOptions {\n  title: string;\n  message?: string;\n  items?: string[];\n  confirmLabel: string;\n  danger?: boolean;\n}\n\nexport class CeConfirmDialogComponent extends CeModal<boolean> {\n  options: ConfirmOptions | undefined;\n\n  override handleAction(action: string): void {\n    const dialog = this.$<HTMLDialogElement>('dialog');\n    if (action === 'confirm') {\n      dialog?.close('confirm');\n    } else if (action === 'cancel') {\n      dialog?.close();\n    }\n  }\n\n  override answer(dialog: HTMLDialogElement): boolean {\n    return dialog.returnValue === 'confirm';\n  }\n\n  override template(): RawHtml {\n    if (!this.options) {\n      return html``;\n    }\n    const { title, message, items, confirmLabel, danger = false } = this.options;\n    return html`\n      <dialog aria-labelledby=\"title\" ${message ? html`aria-describedby=\"message\"` : ''} data-testid=\"dialog\">\n        <header>\n          <h2 id=\"title\" data-testid=\"title\">${title}</h2>\n          ${message ? html`<p id=\"message\" data-testid=\"message\">${message}</p>` : ''}\n        </header>\n        ${\n          items && items.length > 0\n            ? html`<div>\n                <ul data-testid=\"items\">\n                  ${items.map((item) => html`<li>${item}</li>`)}\n                </ul>\n              </div>`\n            : ''\n        }\n        <footer>\n          <button class=\"outline\" data-action=\"cancel\" data-testid=\"cancel\" ${danger ? 'autofocus' : ''}>${t().cancel}</button>\n          <button ${danger ? html`data-variant=\"danger\"` : ''} data-action=\"confirm\" data-testid=\"confirm\" ${danger ? '' : 'autofocus'}>${confirmLabel}</button>\n        </footer>\n      </dialog>\n    `;\n  }\n}\n\nawait define('ce-confirm-dialog', CeConfirmDialogComponent, \"/ui/confirm/ce-confirm-dialog.component.ts\");\n",
    "import type { ConfirmOptions } from './ce-confirm-dialog.component.ts';\nimport { CeConfirmDialogComponent } from './ce-confirm-dialog.component.ts';\n\nexport type { ConfirmOptions };\n\n// Resolves `true` only for the action button; Cancel, Escape and the backdrop resolve `false`.\nexport function confirmAction(options: ConfirmOptions): Promise<boolean> {\n  const dialog = document.createElement('ce-confirm-dialog');\n  if (!(dialog instanceof CeConfirmDialogComponent)) {\n    return Promise.reject(new Error('ce-confirm-dialog is not defined'));\n  }\n  dialog.options = options;\n  return dialog.open();\n}\n",
    "export const PAGE_SIZE = 10;\n\n// The largest `offset` the list endpoints accept.\nexport const MAX_OFFSET = 100000;\n\nexport function parsePage(search: string): number {\n  const value = new URLSearchParams(search).get('page') ?? '';\n  return /^\\d+$/.test(value) && Number(value) >= 1 ? Number(value) : 1;\n}\n\nexport function pageCount(total: number, size: number): number {\n  return Math.min(Math.max(1, Math.ceil(total / size)), Math.floor(MAX_OFFSET / size) + 1);\n}\n\nexport function pageOffset(page: number, size: number): number {\n  return Math.min((page - 1) * size, MAX_OFFSET);\n}\n\nexport function pagePath(path: string, page: number): string {\n  return page === 1 ? path : `${path}?page=${page}`;\n}\n\n// A hole of exactly one page shows that page, which is no wider than a gap.\nexport function pageItems(page: number, pages: number): (number | 'gap')[] {\n  const shown = [...new Set([1, page - 1, page, page + 1, pages])].filter((n) => n >= 1 && n <= pages).toSorted((a, b) => a - b);\n  const items: (number | 'gap')[] = [];\n  let previous = 0;\n  for (const n of shown) {\n    if (n - previous === 2) {\n      items.push(n - 1);\n    } else if (n - previous > 2) {\n      items.push('gap');\n    }\n    items.push(n);\n    previous = n;\n  }\n  return items;\n}\n",
    "import type { RawHtml } from '../html.ts';\nimport { define, CeElement } from '../base.ts';\nimport { html } from '../html.ts';\nimport { t } from '../i18n.ts';\nimport { pageItems } from './pagination.ts';\n\n// Emits `page-change` rather than navigating: ui/ may not import the router.\nclass CePaginationComponent extends CeElement {\n  static observedAttributes = ['page', 'pages'];\n\n  attributeChangedCallback(): void {\n    if (this.isConnected) {\n      this.render();\n    }\n  }\n\n  override handleAction(action: string, element: HTMLElement): void {\n    if (action === 'page') {\n      this.emit('page-change', Number(element.dataset.page));\n    }\n  }\n\n  override template(): RawHtml {\n    const page = Number(this.getAttribute('page') ?? 1);\n    const pages = Number(this.getAttribute('pages') ?? 1);\n    if (page > pages) {\n      return html`\n        <div class=\"vstack\">\n          <p class=\"empty\" data-testid=\"empty\">${t().pagination.emptyPage}</p>\n          <div><button class=\"outline\" data-action=\"page\" data-page=\"1\" data-testid=\"first-page\">${t().pagination.firstPage}</button></div>\n        </div>\n      `;\n    }\n    return html`\n      <nav aria-label=\"Pagination\" data-testid=\"pager\">\n        <menu class=\"buttons\">\n          <li>\n            <button\n              class=\"outline\"\n              data-action=\"page\"\n              data-page=\"${page - 1}\"\n              data-testid=\"previous\"\n              ${page <= 1 ? 'disabled' : ''}\n              aria-label=\"${t().pagination.previous}\"\n            >\n              ←\n            </button>\n          </li>\n          ${pageItems(page, pages).map((item) =>\n            item === 'gap'\n              ? html`<li><button class=\"outline\" disabled aria-hidden=\"true\" data-testid=\"gap\">…</button></li>`\n              : item === page\n                ? html`<li>\n                    <button aria-current=\"page\" data-action=\"page\" data-page=\"${item}\" aria-label=\"${t().pagination.page(item)}\" data-testid=\"page-${item}\">\n                      ${item}\n                    </button>\n                  </li>`\n                : html`<li>\n                    <button class=\"outline\" data-action=\"page\" data-page=\"${item}\" aria-label=\"${t().pagination.page(item)}\" data-testid=\"page-${item}\">\n                      ${item}\n                    </button>\n                  </li>`,\n          )}\n          <li>\n            <button\n              class=\"outline\"\n              data-action=\"page\"\n              data-page=\"${page + 1}\"\n              data-testid=\"next\"\n              ${page >= pages ? 'disabled' : ''}\n              aria-label=\"${t().pagination.next}\"\n            >\n              →\n            </button>\n          </li>\n        </menu>\n      </nav>\n    `;\n  }\n}\n\nawait define('ce-pagination', CePaginationComponent, \"/ui/pagination/ce-pagination.component.ts\");\n",
    "import type { TagDto } from '../../../../shared/dto/tag.ts';\nimport type { TagId } from '../../../../shared/flavors.ts';\nimport { get } from '../../../http/http.ts';\n\nexport class TagApi {\n  list(selected: readonly TagId[] = []): Promise<TagDto[]> {\n    const query = new URLSearchParams(selected.map((id) => ['selected', String(id)]));\n    const search = query.toString();\n    return get(search === '' ? '/tags' : `/tags?${search}`);\n  }\n}\n",
    "import type { TagDto } from '../../../shared/dto/tag.ts';\nimport type { TagId } from '../../../shared/flavors.ts';\nimport { TagApi } from './internal/tag.api.ts';\n\nexport class TagFacade {\n  readonly #api: TagApi;\n\n  constructor(api: TagApi) {\n    this.#api = api;\n  }\n\n  /** Counts are among the recipes carrying every tag in `selected`. */\n  list(selected: readonly TagId[] = []): Promise<TagDto[]> {\n    return this.#api.list(selected);\n  }\n}\n\nexport const tagFacade = new TagFacade(new TagApi());\n",
    "import type { RecipeSortKey, SortOrder } from '../../../../shared/dto/recipe.ts';\nimport type { TagId } from '../../../../shared/flavors.ts';\n\nexport const PAGE_SIZE = 24;\nexport const MAX_SEARCH_LENGTH = 500;\n\n// The backend's allowlist, copied because src/shared/ holds types only; list-query.test.ts pins the two together.\nexport const RECIPE_SORT_KEYS = [\n  'id',\n  'title',\n  'cooked_count',\n  'flagged',\n  'aggregate_rating_value',\n  'rating',\n  'created_at',\n  'updated_at',\n  'last_cooked_at',\n  'prep_time',\n  'cook_time',\n  'total_time',\n  'nutrition_calories',\n  'nutrition_carbohydrate',\n  'nutrition_cholesterol',\n  'nutrition_fat',\n  'nutrition_fiber',\n  'nutrition_protein',\n  'nutrition_saturated_fat',\n  'nutrition_sodium',\n  'nutrition_sugar',\n  'nutrition_trans_fat',\n  'nutrition_unsaturated_fat',\n] as const satisfies readonly RecipeSortKey[];\n\nexport const DEFAULT_SORT: RecipeSortKey = 'title';\n\nexport interface ListQuery {\n  search: string;\n  tags: TagId[];\n  sort: RecipeSortKey;\n  order: SortOrder;\n  page: number;\n  /** Whether the tag panel is open. */\n  tagsOpen: boolean;\n}\n\n// The old server-rendered list wrote `nutrition_saturatedFat` into its sort select.\nconst LEGACY_SORT: Readonly<Record<string, RecipeSortKey>> = { nutrition_saturatedFat: 'nutrition_saturated_fat' };\n\nfunction sortKey(value: string | null): RecipeSortKey {\n  const key = value === null ? undefined : (LEGACY_SORT[value] ?? value);\n  return RECIPE_SORT_KEYS.find((candidate) => candidate === key) ?? DEFAULT_SORT;\n}\n\nfunction ids(values: string[]): TagId[] {\n  const parsed = values.filter((value) => /^\\d+$/.test(value) && Number(value) >= 1).map(Number);\n  return [...new Set(parsed)];\n}\n\n/** Reads `/`'s query string; the old names `title`, `tagId`, `orderBy` and `tagFilter` still work. */\nexport function parseListQuery(search: string): ListQuery {\n  const params = new URLSearchParams(search);\n  const text = (params.get('search') ?? params.get('title') ?? '').trim();\n  const page = params.get('page') ?? '';\n  return {\n    search: text.length > MAX_SEARCH_LENGTH ? '' : text,\n    tags: ids([...params.getAll('tag'), ...params.getAll('tagId')]),\n    sort: sortKey(params.get('sort') ?? params.get('orderBy')),\n    order: (params.get('order') ?? '').toLowerCase() === 'desc' ? 'desc' : 'asc',\n    page: /^\\d+$/.test(page) && Number(page) >= 1 ? Number(page) : 1,\n    tagsOpen: params.has('tags') || params.has('tagFilter'),\n  };\n}\n\n/** The canonical path for `query`, leaving defaults out. */\nexport function listPath(query: ListQuery): string {\n  const params = new URLSearchParams();\n  if (query.search !== '') {\n    params.set('search', query.search);\n  }\n  for (const tag of query.tags) {\n    params.append('tag', String(tag));\n  }\n  if (query.sort !== DEFAULT_SORT) {\n    params.set('sort', query.sort);\n  }\n  if (query.order !== 'asc') {\n    params.set('order', query.order);\n  }\n  if (query.page !== 1) {\n    params.set('page', String(query.page));\n  }\n  if (query.tagsOpen) {\n    params.set('tags', '');\n  }\n  // `tags=` reads better as a bare flag.\n  const search = params.toString().replace(/(^|&)tags=(?=&|$)/, '$1tags');\n  return search === '' ? '/' : `/?${search}`;\n}\n",
    "import { language } from './i18n.ts';\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nconst UNITS: readonly { unit: Intl.RelativeTimeFormatUnit; ms: number }[] = [\n  { unit: 'year', ms: DAY_MS * 365 },\n  { unit: 'month', ms: (DAY_MS * 365) / 12 },\n  { unit: 'day', ms: DAY_MS },\n  { unit: 'hour', ms: 60 * 60 * 1000 },\n  { unit: 'minute', ms: 60 * 1000 },\n  { unit: 'second', ms: 1000 },\n];\n\nfunction closestUnit(ms: number): { unit: Intl.RelativeTimeFormatUnit; amount: number } {\n  const match = UNITS.find((candidate) => Math.abs(ms) >= candidate.ms) ?? { unit: 'second', ms: 1000 };\n  return { unit: match.unit, amount: Math.round(ms / match.ms) };\n}\n\nfunction locale(): string {\n  return language().meta.bcp47;\n}\n\n/** \"3 days ago\", \"yesterday\", \"in 2 hours\", relative to `now`. */\nexport function formatRelative(date: Date | string, now: Date = new Date()): string {\n  const { unit, amount } = closestUnit(new Date(date).getTime() - now.getTime());\n  return new Intl.RelativeTimeFormat(locale(), { numeric: 'auto' }).format(amount, unit);\n}\n\n/** \"35 minutes\", \"2 hours\": the closest whole unit. */\nexport function formatMinutes(minutes: number): string {\n  const { unit, amount } = closestUnit(minutes * 60 * 1000);\n  return new Intl.NumberFormat(locale(), { style: 'unit', unit, unitDisplay: 'long' }).format(amount);\n}\n\nexport function formatNumber(value: number, decimals = 2): string {\n  return new Intl.NumberFormat(locale(), { minimumFractionDigits: decimals, maximumFractionDigits: decimals }).format(value);\n}\n\n/** Up to `maxDigits` decimals, none for a whole number: 400, 0.333, 2.5. */\nexport function formatDecimal(value: number, maxDigits = 3): string {\n  return new Intl.NumberFormat(locale(), { maximumFractionDigits: maxDigits }).format(value);\n}\n\nexport function formatDate(date: Date | string, options: Intl.DateTimeFormatOptions = { dateStyle: 'medium' }): string {\n  return new Intl.DateTimeFormat(locale(), options).format(new Date(date));\n}\n",
    "import type { RecipeSummaryDto } from '../../../../shared/dto/recipe.ts';\nimport type { RawHtml } from '../../../ui/html.ts';\nimport { define, CeElement } from '../../../ui/base.ts';\nimport { formatDate, formatMinutes, formatNumber, formatRelative } from '../../../ui/format.ts';\nimport { html, raw } from '../../../ui/html.ts';\nimport { t } from '../../../ui/i18n.ts';\n\nexport const PLACEHOLDER_THUMBNAIL = '/ui/placeholder.svg';\n\nexport class CeRecipeCardComponent extends CeElement {\n  #recipe: RecipeSummaryDto | null = null;\n\n  set recipe(value: RecipeSummaryDto | undefined) {\n    this.#recipe = value ?? null;\n    if (this.isConnected) {\n      this.render();\n    }\n  }\n\n  get recipe(): RecipeSummaryDto | undefined {\n    return this.#recipe ?? undefined;\n  }\n\n  override handleAction(action: string): void {\n    const recipe = this.#recipe;\n    if (action === 'delete' && recipe) {\n      this.emit('recipe-delete', recipe.id);\n    }\n  }\n\n  #stats(recipe: RecipeSummaryDto): string {\n    const l = t();\n    const parts = [l.recipe.cookedTimes(recipe.cookedCount)];\n    if (recipe.rating > 0) {\n      parts.push(`★ ${formatNumber(recipe.rating, 1)}`);\n    }\n    if (recipe.aggregateRating.value !== null) {\n      parts.push(`👥 ${formatNumber(recipe.aggregateRating.value)}`);\n    }\n    if (recipe.totalTime > 0) {\n      parts.push(formatMinutes(recipe.totalTime));\n    }\n    return parts.join(' · ');\n  }\n\n  override template(): RawHtml {\n    const recipe = this.#recipe;\n    if (!recipe) {\n      return raw('');\n    }\n    const l = t();\n    return html`\n      <article class=\"card open-card${recipe.flagged ? ' flagged' : ''}\" data-testid=\"card\">\n        <img class=\"thumb\" src=\"${recipe.thumbnail ?? PLACEHOLDER_THUMBNAIL}\" alt=\"\" loading=\"lazy\" data-testid=\"thumbnail\" />\n        <div class=\"head\">\n          <a class=\"open\" href=\"/recipes/${recipe.id}\" data-testid=\"open\">\n            ${recipe.flagged ? html`<span class=\"flag\" title=\"${l.recipe.flagged}\" data-testid=\"flag\">⚑</span>` : ''}${recipe.title}\n          </a>\n          <div class=\"actions\">\n            <ot-dropdown>\n              <button type=\"button\" class=\"ghost icon\" popovertarget=\"menu\" aria-label=\"${l.recipe.actions}\" data-testid=\"menu-button\">\n                <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n                  <circle cx=\"5\" cy=\"12\" r=\"2\" />\n                  <circle cx=\"12\" cy=\"12\" r=\"2\" />\n                  <circle cx=\"19\" cy=\"12\" r=\"2\" />\n                </svg>\n              </button>\n              <menu popover id=\"menu\" data-testid=\"menu\">\n                <a role=\"menuitem\" href=\"/recipes/${recipe.id}/edit\" data-testid=\"edit\">${l.edit}</a>\n                <button type=\"button\" role=\"menuitem\" class=\"ghost\" data-variant=\"danger\" data-action=\"delete\" data-testid=\"delete\">${l.delete}</button>\n              </menu>\n            </ot-dropdown>\n          </div>\n        </div>\n        ${recipe.description === '' ? '' : html`<p class=\"description text-light\" data-testid=\"description\">${recipe.description}</p>`}\n        <small\n          class=\"text-light\"\n          data-testid=\"cooked\"\n          ${recipe.lastCookedAt === null ? '' : html`title=\"${formatDate(recipe.lastCookedAt, { dateStyle: 'full', timeStyle: 'short' })}\"`}\n        >\n          ${recipe.lastCookedAt === null ? l.recipe.notCookedYet : l.recipe.lastCooked(formatRelative(recipe.lastCookedAt))}\n        </small>\n        <div class=\"foot\">\n          <span class=\"text-light\" data-testid=\"stats\">${this.#stats(recipe)}</span>\n        </div>\n      </article>\n    `;\n  }\n}\n\nawait define('ce-recipe-card', CeRecipeCardComponent, \"/features/recipes/internal/ce-recipe-card.component.ts\");\n",
    "// Based on https://gist.github.com/sumn2u/0e0b5d9505ad096284928a987ace13fb\nexport function jaroWinklerDistance(s1: string, s2: string): number {\n  if (s1.length === 0 || s2.length === 0) {\n    return 0;\n  }\n  if (s1 === s2) {\n    return 1;\n  }\n\n  const range = Math.floor(Math.max(s1.length, s2.length) / 2) - 1;\n  const s1Matches: boolean[] = Array.from({ length: s1.length }, () => false);\n  const s2Matches: boolean[] = Array.from({ length: s2.length }, () => false);\n  let matches = 0;\n\n  for (let i = 0; i < s1.length; i++) {\n    const low = i >= range ? i - range : 0;\n    const high = i + range <= s2.length ? i + range : s2.length - 1;\n    for (let j = low; j <= high; j++) {\n      if (s1Matches[i] !== true && s2Matches[j] !== true && s1[i] === s2[j]) {\n        matches++;\n        s1Matches[i] = true;\n        s2Matches[j] = true;\n        break;\n      }\n    }\n  }\n  if (matches === 0) {\n    return 0;\n  }\n\n  let k = 0;\n  let transpositions = 0;\n  for (let i = 0; i < s1.length; i++) {\n    if (s1Matches[i] === true) {\n      let j = k;\n      for (; j < s2.length; j++) {\n        if (s2Matches[j] === true) {\n          k = j + 1;\n          break;\n        }\n      }\n      if (s1[i] !== s2[j]) {\n        transpositions++;\n      }\n    }\n  }\n\n  let weight = (matches / s1.length + matches / s2.length + (matches - transpositions / 2) / matches) / 3;\n  if (weight > 0.7) {\n    let prefix = 0;\n    while (prefix < 4 && s1[prefix] === s2[prefix]) {\n      prefix++;\n    }\n    weight += prefix * 0.1 * (1 - weight);\n  }\n  return weight;\n}\n",
    "import { jaroWinklerDistance } from './jaro-winkler.ts';\n\nexport const SIMILARITY_THRESHOLD = 0.75;\n\nexport function matchesFilter(title: string, filter: string): boolean {\n  const query = filter.trim().toLowerCase();\n  return query === '' || jaroWinklerDistance(query, title.trim().toLowerCase()) >= SIMILARITY_THRESHOLD;\n}\n",
    "import type { TagDto } from '../../../../shared/dto/tag.ts';\nimport type { TagId } from '../../../../shared/flavors.ts';\nimport type { RawHtml } from '../../../ui/html.ts';\nimport { define, CeElement } from '../../../ui/base.ts';\nimport { html } from '../../../ui/html.ts';\nimport { t } from '../../../ui/i18n.ts';\nimport { matchesFilter } from './tag-filter.ts';\n\n/** Emits `tag-toggle` (the id), `tags-clear` and `tags-open` (whether the panel is open); its owner decides what they change. */\nexport class CeTagPanelComponent extends CeElement {\n  #tags: TagDto[] = [];\n  #selected: readonly TagId[] = [];\n  #open = false;\n  #filter = '';\n\n  set state(value: { tags: TagDto[]; selected: readonly TagId[]; open: boolean }) {\n    this.#tags = value.tags;\n    this.#selected = value.selected;\n    this.#open = value.open;\n    if (this.isConnected) {\n      const focused = this.root.activeElement;\n      const focusedTag = focused instanceof HTMLElement ? focused.dataset.tagId : undefined;\n      const filterFocused = focused instanceof HTMLInputElement;\n      this.render();\n      // A toggle re-renders the panel; the next click or keystroke lands where the last one did.\n      if (focusedTag !== undefined) {\n        this.$<HTMLElement>(`[data-tag-id=\"${focusedTag}\"]`)?.focus();\n      } else if (filterFocused) {\n        this.$<HTMLInputElement>('input[type=\"search\"]')?.focus();\n      }\n    }\n  }\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.root.addEventListener('input', (event) => {\n      if (event.target instanceof HTMLInputElement && event.target.type === 'search') {\n        this.#filter = event.target.value;\n        this.#applyFilter();\n      }\n    });\n  }\n\n  override afterRender(): void {\n    this.$('details')?.addEventListener('toggle', (event) => {\n      const open = event.target instanceof HTMLDetailsElement && event.target.open;\n      if (open !== this.#open) {\n        this.#open = open;\n        this.emit('tags-open', open);\n      }\n    });\n    this.#applyFilter();\n  }\n\n  override handleAction(action: string, element: HTMLElement): void {\n    if (action === 'toggle') {\n      this.emit('tag-toggle', Number(element.dataset.tagId));\n    } else if (action === 'clear') {\n      this.emit('tags-clear');\n    }\n  }\n\n  // Selected tags never hide, so a selection can always be undone.\n  #applyFilter(): void {\n    for (const button of this.$$<HTMLButtonElement>('button[data-tag-id]')) {\n      const selected = button.getAttribute('aria-pressed') === 'true';\n      button.hidden = !selected && !matchesFilter(button.dataset.title ?? '', this.#filter);\n    }\n  }\n\n  override template(): RawHtml {\n    const l = t();\n    const selected = new Set(this.#selected);\n    return html`\n      <details data-testid=\"details\" ${this.#open ? 'open' : ''}>\n        <summary data-testid=\"summary\">${l.recipe.tagsSelected(selected.size)}</summary>\n        <div class=\"body vstack gap-3\">\n          <div class=\"hstack gap-2\">\n            <input\n              type=\"search\"\n              class=\"grow\"\n              placeholder=\"${l.navigation.filterPlaceholder}\"\n              aria-label=\"${l.navigation.filterTitle}\"\n              value=\"${this.#filter}\"\n              autocomplete=\"off\"\n              data-testid=\"filter\"\n            />\n            <button type=\"button\" class=\"outline\" data-action=\"clear\" data-testid=\"clear\" ${selected.size === 0 ? 'disabled' : ''}>\n              ${l.recipe.clearAllTags}\n            </button>\n          </div>\n          ${\n            this.#tags.length === 0\n              ? html`<p class=\"text-light\" data-testid=\"no-tags\">${l.recipe.noTags}</p>`\n              : html`<div class=\"tags\" data-testid=\"tags\">\n                  ${this.#tags.map((tag) => {\n                    const pressed = selected.has(tag.id);\n                    return html`<button\n                      type=\"button\"\n                      class=\"${pressed ? '' : 'outline'}\"\n                      aria-pressed=\"${pressed ? 'true' : 'false'}\"\n                      data-action=\"toggle\"\n                      data-tag-id=\"${tag.id}\"\n                      data-title=\"${tag.title}\"\n                      data-testid=\"tag-${tag.id}\"\n                      ${!pressed && tag.recipeCount === 0 ? 'disabled' : ''}\n                    >\n                      ${tag.title} <span class=\"count\">${tag.recipeCount}</span>\n                    </button>`;\n                  })}\n                </div>`\n          }\n        </div>\n      </details>\n    `;\n  }\n}\n\nawait define('ce-tag-panel', CeTagPanelComponent, \"/features/recipes/internal/ce-tag-panel.component.ts\");\n",
    "import type {\n  CreateRecipeDto,\n  EditRecipeDto,\n  ImportRecipesDto,\n  ImportResultDto,\n  RecipeDto,\n  RecipeFilterDto,\n  RecipePageDto,\n  RecipeSortDto,\n} from '../../../../shared/dto/recipe.ts';\nimport type { RecipeId } from '../../../../shared/flavors.ts';\nimport { get, patch, post, putBlob, remove, removeReturning } from '../../../http/http.ts';\n\nexport type RecipeListRequest = RecipeFilterDto & Partial<RecipeSortDto> & { limit: number; offset: number };\n\nexport class RecipeApi {\n  list({ limit, offset, search, tags, sort, order }: RecipeListRequest): Promise<RecipePageDto> {\n    const query = new URLSearchParams({ limit: String(limit), offset: String(offset) });\n    if (search !== undefined && search !== '') {\n      query.set('search', search);\n    }\n    for (const tag of tags ?? []) {\n      query.append('tag', String(tag));\n    }\n    if (sort !== undefined) {\n      query.set('sort', sort);\n    }\n    if (order !== undefined) {\n      query.set('order', order);\n    }\n    return get(`/recipes?${query.toString()}`);\n  }\n\n  import(dto: ImportRecipesDto): Promise<ImportResultDto> {\n    return post('/recipes/import', dto);\n  }\n\n  get(id: RecipeId): Promise<RecipeDto> {\n    return get(`/recipes/${id}`);\n  }\n\n  create(dto: CreateRecipeDto): Promise<RecipeDto> {\n    return post('/recipes', dto);\n  }\n\n  update(id: RecipeId, dto: EditRecipeDto): Promise<RecipeDto> {\n    return patch(`/recipes/${id}`, dto);\n  }\n\n  delete(id: RecipeId): Promise<null> {\n    return remove(`/recipes/${id}`);\n  }\n\n  putThumbnail(id: RecipeId, image: Blob): Promise<RecipeDto> {\n    return putBlob(`/recipes/${id}/thumbnail`, image);\n  }\n\n  deleteThumbnail(id: RecipeId): Promise<RecipeDto> {\n    return removeReturning(`/recipes/${id}/thumbnail`);\n  }\n}\n",
    "import type { CreateRecipeDto, EditRecipeDto, ImportRecipesDto, ImportResultDto, RecipeDto, RecipePageDto } from '../../../shared/dto/recipe.ts';\nimport type { RecipeId } from '../../../shared/flavors.ts';\nimport { RecipeApi, type RecipeListRequest } from './internal/recipe.api.ts';\n\nexport class RecipeFacade {\n  readonly #api: RecipeApi;\n\n  constructor(api: RecipeApi) {\n    this.#api = api;\n  }\n\n  list(request: RecipeListRequest): Promise<RecipePageDto> {\n    return this.#api.list(request);\n  }\n\n  import(dto: ImportRecipesDto): Promise<ImportResultDto> {\n    return this.#api.import(dto);\n  }\n\n  get(id: RecipeId): Promise<RecipeDto> {\n    return this.#api.get(id);\n  }\n\n  create(dto: CreateRecipeDto): Promise<RecipeDto> {\n    return this.#api.create(dto);\n  }\n\n  update(id: RecipeId, dto: EditRecipeDto): Promise<RecipeDto> {\n    return this.#api.update(id, dto);\n  }\n\n  delete(id: RecipeId): Promise<null> {\n    return this.#api.delete(id);\n  }\n\n  putThumbnail(id: RecipeId, image: Blob): Promise<RecipeDto> {\n    return this.#api.putThumbnail(id, image);\n  }\n\n  deleteThumbnail(id: RecipeId): Promise<RecipeDto> {\n    return this.#api.deleteThumbnail(id);\n  }\n}\n\nexport const recipeFacade = new RecipeFacade(new RecipeApi());\n",
    "import type { RecipePageDto, RecipeSortKey, RecipeSummaryDto } from '../../../shared/dto/recipe.ts';\nimport type { TagDto } from '../../../shared/dto/tag.ts';\nimport type { Language } from '../../i18n/language.ts';\nimport type { RawHtml } from '../../ui/html.ts';\nimport { define } from '../../ui/base.ts';\nimport { confirmAction } from '../../ui/confirm/confirm.ts';\nimport { html } from '../../ui/html.ts';\nimport { t } from '../../ui/i18n.ts';\nimport { pageCount, pageOffset } from '../../ui/pagination/pagination.ts';\nimport '../../ui/pagination/ce-pagination.component.ts';\nimport { toast, toastError } from '../../ui/toast.ts';\nimport { CeView } from '../../ui/view.ts';\nimport { tagFacade } from '../tags/tags.facade.ts';\nimport { type ListQuery, listPath, PAGE_SIZE, parseListQuery, RECIPE_SORT_KEYS } from './internal/list-query.ts';\nimport type { CeRecipeCardComponent } from './internal/ce-recipe-card.component.ts';\nimport './internal/ce-recipe-card.component.ts';\nimport type { CeTagPanelComponent } from './internal/ce-tag-panel.component.ts';\nimport './internal/ce-tag-panel.component.ts';\nimport { recipeFacade } from './recipes.facade.ts';\n\nconst SEARCH_DEBOUNCE_MS = 300;\n\ninterface RecipeListData {\n  query: ListQuery;\n  recipes: RecipePageDto;\n  tags: TagDto[];\n}\n\nfunction sortGroups(l: Language): { label: string; options: [RecipeSortKey, string][] }[] {\n  const n = l.recipe.nutrition;\n  return [\n    {\n      label: l.recipe.form.group.basic,\n      options: [\n        ['id', l.id],\n        ['title', l.title],\n        ['cooked_count', l.recipe.cookedCount],\n        ['flagged', l.recipe.flagged],\n      ],\n    },\n    {\n      label: l.recipe.form.group.ratings,\n      options: [\n        ['aggregate_rating_value', l.recipe.aggregateRatingValue],\n        ['rating', l.recipe.rating],\n      ],\n    },\n    {\n      label: l.recipe.form.group.times,\n      options: [\n        ['created_at', l.createdAt],\n        ['updated_at', l.updatedAt],\n        ['last_cooked_at', l.recipe.lastCookedAt],\n        ['prep_time', l.recipe.prepTime],\n        ['cook_time', l.recipe.cookTime],\n        ['total_time', l.recipe.time.total],\n      ],\n    },\n    {\n      label: l.recipe.form.group.nutrition,\n      options: [\n        ['nutrition_calories', n.calories],\n        ['nutrition_carbohydrate', n.carbohydrate],\n        ['nutrition_cholesterol', n.cholesterol],\n        ['nutrition_fat', n.fat],\n        ['nutrition_fiber', n.fiber],\n        ['nutrition_protein', n.protein],\n        ['nutrition_saturated_fat', n.saturatedFat],\n        ['nutrition_sodium', n.sodium],\n        ['nutrition_sugar', n.sugar],\n        ['nutrition_trans_fat', n.transFat],\n        ['nutrition_unsaturated_fat', n.unsaturatedFat],\n      ],\n    },\n  ];\n}\n\n/** List state lives in the query string; a change is a pushState and a refetch that patches the results, never a re-render of the toolbar. */\nexport class CeRecipeListComponent extends CeView<RecipeListData> {\n  #searchTimer: ReturnType<typeof setTimeout> | undefined;\n  #requestToken = 0;\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.root.addEventListener('input', (event) => {\n      if (event.target instanceof HTMLInputElement && event.target.name === 'search') {\n        const field = event.target;\n        clearTimeout(this.#searchTimer);\n        this.#searchTimer = setTimeout(() => {\n          this.#applySearch(field.value);\n        }, SEARCH_DEBOUNCE_MS);\n      }\n    });\n    // Chrome and Safari fire only `search`, not `input`, when the ✕ clears the field.\n    this.root.addEventListener('search', (event) => {\n      if (event.target instanceof HTMLInputElement && event.target.name === 'search') {\n        this.#applySearch(event.target.value);\n      }\n    });\n    this.root.addEventListener('change', (event) => {\n      const { target } = event;\n      if (target instanceof HTMLSelectElement && target.name === 'sort') {\n        const sort = RECIPE_SORT_KEYS.find((key) => key === target.value);\n        if (sort !== undefined) {\n          this.#change({ sort, page: 1 });\n        }\n      }\n    });\n    this.root.addEventListener('page-change', (event) => {\n      if (event instanceof CustomEvent && typeof event.detail === 'number') {\n        this.#change({ page: event.detail });\n        window.scrollTo({ top: 0, behavior: 'instant' });\n      }\n    });\n    this.root.addEventListener('tag-toggle', (event) => {\n      if (event instanceof CustomEvent && typeof event.detail === 'number') {\n        const tags = this.#query().tags;\n        const id = event.detail;\n        this.#change({ tags: tags.includes(id) ? tags.filter((tag) => tag !== id) : [...tags, id], page: 1 });\n      }\n    });\n    this.root.addEventListener('recipe-delete', (event) => {\n      const recipe = event instanceof CustomEvent ? this.data?.recipes.items.find((item) => item.id === event.detail) : undefined;\n      if (recipe) {\n        void this.#delete(recipe);\n      }\n    });\n    this.root.addEventListener('tags-clear', () => {\n      this.#change({ tags: [], page: 1 });\n    });\n    this.root.addEventListener('tags-open', (event) => {\n      if (event instanceof CustomEvent && typeof event.detail === 'boolean') {\n        this.#replace({ tagsOpen: event.detail });\n      }\n    });\n  }\n\n  override disconnectedCallback(): void {\n    super.disconnectedCallback();\n    clearTimeout(this.#searchTimer);\n  }\n\n  override async load(): Promise<RecipeListData> {\n    return this.#fetch(parseListQuery(location.search));\n  }\n\n  override handleSubmit(action: string, form: HTMLFormElement): void {\n    if (action === 'search') {\n      clearTimeout(this.#searchTimer);\n      this.#applySearch(this.formData(form).search ?? '');\n    }\n  }\n\n  override handleAction(action: string): void {\n    if (action === 'order') {\n      this.#change({ order: this.#query().order === 'asc' ? 'desc' : 'asc', page: 1 });\n    }\n  }\n\n  override afterRender(): void {\n    this.#patch();\n  }\n\n  override titleFor(): string {\n    return t().recipes;\n  }\n\n  #query(): ListQuery {\n    return this.data?.query ?? parseListQuery(location.search);\n  }\n\n  async #fetch(query: ListQuery): Promise<RecipeListData> {\n    const [recipes, tags] = await Promise.all([\n      recipeFacade.list({\n        limit: PAGE_SIZE,\n        offset: pageOffset(query.page, PAGE_SIZE),\n        search: query.search,\n        tags: query.tags,\n        sort: query.sort,\n        order: query.order,\n      }),\n      tagFacade.list(query.tags),\n    ]);\n    return { query, recipes, tags };\n  }\n\n  #applySearch(value: string): void {\n    const search = value.trim();\n    if (search !== this.#query().search) {\n      this.#change({ search, page: 1 });\n    }\n  }\n\n  // The panel's open state changes no results, so it only rewrites the URL.\n  #replace(change: Partial<ListQuery>): void {\n    const data = this.data;\n    if (!data) {\n      return;\n    }\n    const query = { ...data.query, ...change };\n    history.replaceState(null, '', listPath(query));\n    this.replaceData({ ...data, query });\n  }\n\n  #change(change: Partial<ListQuery>): void {\n    const query = { ...this.#query(), ...change };\n    const path = listPath(query);\n    if (location.pathname + location.search !== path) {\n      history.pushState(null, '', path);\n    }\n    void this.#refresh(query);\n  }\n\n  async #delete(recipe: RecipeSummaryDto): Promise<void> {\n    const l = t();\n    const confirmed = await confirmAction({\n      title: l.recipe.deleteTitle(recipe.title),\n      message: l.recipe.deleteConfirmation,\n      confirmLabel: l.delete,\n      danger: true,\n    });\n    if (!confirmed) {\n      return;\n    }\n    try {\n      await recipeFacade.delete(recipe.id);\n    } catch (error) {\n      toastError(error);\n      return;\n    }\n    toast(l.recipe.deleteSuccessful, 'success');\n    await this.#refresh(this.#query());\n  }\n\n  async #refresh(query: ListQuery): Promise<void> {\n    const token = ++this.#requestToken;\n    this.$('[data-testid=\"results\"]')?.setAttribute('aria-busy', 'true');\n    let data: RecipeListData;\n    try {\n      data = await this.#fetch(query);\n    } catch (error) {\n      toastError(error);\n      return;\n    } finally {\n      if (token === this.#requestToken) {\n        this.$('[data-testid=\"results\"]')?.removeAttribute('aria-busy');\n      }\n    }\n    if (token !== this.#requestToken) {\n      return;\n    }\n    this.replaceData(data);\n    this.#patch();\n  }\n\n  // Fills what depends on the data: the count, the order button, the tag panel and the results.\n  #patch(): void {\n    const data = this.data;\n    if (!data) {\n      return;\n    }\n    const l = t();\n    const { query, recipes, tags } = data;\n    const count = this.$('[data-testid=\"count\"]');\n    if (count) {\n      count.textContent = l.recipe.count(recipes.total);\n    }\n    const order = this.$<HTMLButtonElement>('[data-testid=\"order\"]');\n    if (order) {\n      order.textContent = query.order === 'asc' ? '↓' : '↑';\n      order.setAttribute('aria-label', query.order === 'asc' ? l.orderBy.desc : l.orderBy.asc);\n      order.title = order.getAttribute('aria-label') ?? '';\n    }\n    const sort = this.$<HTMLSelectElement>('select[name=\"sort\"]');\n    if (sort) {\n      sort.value = query.sort;\n    }\n    const panel = this.$<CeTagPanelComponent>('ce-tag-panel');\n    if (panel) {\n      panel.state = { tags, selected: query.tags, open: query.tagsOpen };\n    }\n    const results = this.$('[data-testid=\"results\"]');\n    if (results) {\n      results.innerHTML = String(this.#results(data));\n      const cards = this.$$<CeRecipeCardComponent>('ce-recipe-card');\n      cards.forEach((card, index) => {\n        card.recipe = recipes.items[index];\n      });\n    }\n  }\n\n  #results({ query, recipes }: RecipeListData): RawHtml {\n    const pages = pageCount(recipes.total, PAGE_SIZE);\n    const pager = html`<ce-pagination page=\"${query.page}\" pages=\"${pages}\" data-testid=\"pagination\"></ce-pagination>`;\n    if (query.page > pages) {\n      return pager;\n    }\n    if (recipes.items.length === 0) {\n      return html`<p class=\"empty\" role=\"status\" data-testid=\"empty\">${t().recipe.noRecipesFound}</p>`;\n    }\n    return html`\n      <div class=\"grid-cards\" data-testid=\"cards\">\n        ${recipes.items.map((recipe) => html`<ce-recipe-card data-testid=\"card-${recipe.id}\"></ce-recipe-card>`)}\n      </div>\n      ${pages > 1 ? pager : ''}\n    `;\n  }\n\n  override readyTemplate({ query }: RecipeListData): RawHtml {\n    const l = t();\n    return html`\n      <div class=\"vstack gap-4\">\n        <div class=\"hstack justify-between gap-2\">\n          <hgroup>\n            <h1>${l.recipes}</h1>\n            <p class=\"text-light\" data-testid=\"count\"></p>\n          </hgroup>\n          <div class=\"hstack gap-2\">\n            <a class=\"button outline\" href=\"/recipes/import\" data-testid=\"import\">${l.recipe.import.title}</a>\n            <a class=\"button\" href=\"/recipes/new\" data-testid=\"new\">+ ${l.recipe.newRecipe}</a>\n          </div>\n        </div>\n        <div class=\"filters\">\n          <form class=\"search\" data-action=\"search\" role=\"search\">\n            <input\n              type=\"search\"\n              name=\"search\"\n              value=\"${query.search}\"\n              placeholder=\"${l.search}\"\n              aria-label=\"${l.search}\"\n              maxlength=\"500\"\n              data-testid=\"search\"\n            />\n          </form>\n          <div class=\"sort hstack gap-2\">\n            <select name=\"sort\" aria-label=\"${l.orderBy.title}\" data-testid=\"sort\">\n              ${sortGroups(l).map(\n                (group) =>\n                  html`<optgroup label=\"${group.label}\">\n                    ${group.options.map(([key, label]) => html`<option value=\"${key}\" ${key === query.sort ? 'selected' : ''}>${label}</option>`)}\n                  </optgroup>`,\n              )}\n            </select>\n            <button type=\"button\" class=\"outline\" data-action=\"order\" data-testid=\"order\"></button>\n          </div>\n        </div>\n        <ce-tag-panel data-testid=\"tag-panel\"></ce-tag-panel>\n        <div class=\"results vstack gap-4\" data-testid=\"results\"></div>\n      </div>\n    `;\n  }\n}\n\nawait define('ce-recipe-list', CeRecipeListComponent, \"/features/recipes/ce-recipe-list.component.ts\");\n",
    "import { confirmAction } from './confirm/confirm.ts';\nimport { t } from './i18n.ts';\n\nconst guards = new Set<() => boolean>();\n\nconst onBeforeUnload = (event: BeforeUnloadEvent): void => {\n  if (hasUnsavedChanges()) {\n    event.preventDefault();\n  }\n};\n\n/** While `isDirty()` holds, closing the tab asks the browser's question and an in-app navigation asks ours. Returns the release. */\nexport function guardLeave(isDirty: () => boolean): () => void {\n  if (guards.size === 0) {\n    window.addEventListener('beforeunload', onBeforeUnload);\n  }\n  guards.add(isDirty);\n  return () => {\n    guards.delete(isDirty);\n    if (guards.size === 0) {\n      window.removeEventListener('beforeunload', onBeforeUnload);\n    }\n  };\n}\n\nexport function hasUnsavedChanges(): boolean {\n  return [...guards].some((isDirty) => isDirty());\n}\n\n/** Resolves true at once without unsaved changes; once confirmed, the guards stop asking, so the navigation that follows goes through. */\nexport async function confirmLeave(): Promise<boolean> {\n  if (!hasUnsavedChanges()) {\n    return true;\n  }\n  const l = t();\n  const confirmed = await confirmAction({ title: l.leave.title, message: l.leave.message, confirmLabel: l.leave.discard, danger: true });\n  if (confirmed) {\n    guards.clear();\n    window.removeEventListener('beforeunload', onBeforeUnload);\n  }\n  return confirmed;\n}\n",
    "import type { Iso8601DateTime } from '../../../../shared/flavors.ts';\n\nconst pad = (n: number): string => String(n).padStart(2, '0');\n\n/** The local date and time inputs for an ISO date-time. */\nexport function toLocalParts(iso: string): { date: string; time: string } {\n  const value = new Date(iso);\n  return {\n    date: `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`,\n    time: `${pad(value.getHours())}:${pad(value.getMinutes())}`,\n  };\n}\n\n/** Local date and time back to an ISO date-time; null when the date is empty or invalid. A missing time is midnight. */\nexport function fromLocalParts(date: string, time: string): Iso8601DateTime | null {\n  if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(date)) {\n    return null;\n  }\n  const value = new Date(`${date}T${/^\\d{2}:\\d{2}$/.test(time) ? time : '00:00'}`);\n  return Number.isNaN(value.getTime()) ? null : value.toISOString();\n}\n",
    "import type { Iso8601DateTime } from '../../../../shared/flavors.ts';\nimport type { RawHtml } from '../../../ui/html.ts';\nimport { define, CeElement } from '../../../ui/base.ts';\nimport { html } from '../../../ui/html.ts';\nimport { t } from '../../../ui/i18n.ts';\nimport { fromLocalParts, toLocalParts } from './history-time.ts';\n\ninterface Entry {\n  date: string;\n  time: string;\n  /** The stored value, kept while the row is untouched so its seconds survive a save. */\n  original?: string;\n}\n\nfunction toEntry(iso: string): Entry {\n  return { ...toLocalParts(iso), original: iso };\n}\n\nfunction isoOf(entry: Entry): Iso8601DateTime | null {\n  if (entry.original !== undefined) {\n    const parts = toLocalParts(entry.original);\n    if (parts.date === entry.date && parts.time === entry.time) {\n      return entry.original;\n    }\n  }\n  return fromLocalParts(entry.date, entry.time);\n}\n\n/** When the recipe was cooked: one date and time per entry, in local time. Emits `input` on any change. */\nexport class CeHistoryFieldComponent extends CeElement {\n  static observedAttributes = ['label'];\n\n  #entries: Entry[] = [];\n\n  attributeChangedCallback(): void {\n    if (this.isConnected) {\n      this.#rerender();\n    }\n  }\n\n  set value(entries: readonly string[]) {\n    this.#entries = entries.map(toEntry);\n    if (this.isConnected) {\n      this.render();\n    }\n  }\n\n  /** Newest first, as the API answers them; rows without a date are left out. */\n  get value(): Iso8601DateTime[] {\n    const entries = this.isConnected ? this.#read() : this.#entries;\n    return entries\n      .map(isoOf)\n      .filter((entry): entry is Iso8601DateTime => entry !== null)\n      .toSorted((a, b) => b.localeCompare(a));\n  }\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.root.addEventListener('input', (event) => {\n      event.stopPropagation();\n      this.#changed();\n    });\n    this.root.addEventListener('change', (event) => {\n      event.stopPropagation();\n      this.#changed();\n    });\n  }\n\n  override languageChanged(): void {\n    this.#rerender();\n  }\n\n  override handleAction(action: string, element: HTMLElement): void {\n    const entries = this.#read();\n    if (action === 'add') {\n      entries.unshift(toLocalParts(new Date().toISOString()));\n    } else if (action === 'remove') {\n      entries.splice(Number(element.dataset.index), 1);\n    } else {\n      return;\n    }\n    this.#entries = entries;\n    this.render();\n    this.#changed();\n  }\n\n  #changed(): void {\n    this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n  }\n\n  #rerender(): void {\n    this.#entries = this.#read();\n    this.render();\n  }\n\n  #read(): Entry[] {\n    if (this.$('[data-testid=\"add\"]') === null) {\n      return [...this.#entries];\n    }\n    return this.$$<HTMLElement>('[data-testid=\"entry\"]').map((row) => ({\n      date: row.querySelector<HTMLInputElement>('input[type=\"date\"]')?.value ?? '',\n      time: row.querySelector<HTMLInputElement>('input[type=\"time\"]')?.value ?? '',\n      ...(row.dataset.original === undefined ? {} : { original: row.dataset.original }),\n    }));\n  }\n\n  override template(): RawHtml {\n    const l = t();\n    return html`\n      <fieldset class=\"vstack gap-2\">\n        <legend>${this.getAttribute('label') ?? ''}</legend>\n        <ol class=\"unstyled vstack gap-2\" data-testid=\"entries\">\n          ${this.#entries.map((entry, i) => {\n            const label = l.recipe.form.historyEntry(i + 1);\n            return html`<li class=\"entry\" data-testid=\"entry\" ${entry.original === undefined ? '' : html`data-original=\"${entry.original}\"`}>\n              <input type=\"date\" value=\"${entry.date}\" aria-label=\"${label}: ${l.recipe.form.date}\" data-testid=\"date-${i}\" />\n              <input type=\"time\" value=\"${entry.time}\" aria-label=\"${label}: ${l.recipe.form.time}\" data-testid=\"time-${i}\" />\n              <button\n                type=\"button\"\n                class=\"outline icon\"\n                data-variant=\"danger\"\n                data-action=\"remove\"\n                data-index=\"${i}\"\n                aria-label=\"${l.recipe.form.remove}: ${label}\"\n              >\n                ×\n              </button>\n            </li>`;\n          })}\n        </ol>\n        <div><button type=\"button\" class=\"outline\" data-action=\"add\" data-testid=\"add\">+ ${l.recipe.form.addHistoryEntry}</button></div>\n      </fieldset>\n    `;\n  }\n}\n\nawait define('ce-history-field', CeHistoryFieldComponent, \"/features/recipes/internal/ce-history-field.component.ts\");\n",
    "import type { RawHtml } from '../../../ui/html.ts';\nimport { define, CeElement } from '../../../ui/base.ts';\nimport { html } from '../../../ui/html.ts';\nimport { t } from '../../../ui/i18n.ts';\n\n/**\n * An editable list of lines: add, remove, up and down. `multiline` makes each a textarea.\n * Emits `input` whenever the list changes. The DOM holds the truth while editing; `value` reads it back.\n */\nexport class CeListFieldComponent extends CeElement {\n  static observedAttributes = ['multiline', 'label', 'add-label', 'maxlength'];\n\n  #items: string[] = [];\n  // Labels such as \"Ingredient 3\"; set by the owner because the strings live in its dictionary keys.\n  #itemLabel: (n: number) => string = (n) => String(n);\n\n  attributeChangedCallback(): void {\n    if (this.isConnected) {\n      this.#rerender();\n    }\n  }\n\n  set itemLabel(label: (n: number) => string) {\n    this.#itemLabel = label;\n  }\n\n  set value(items: readonly string[]) {\n    this.#items = [...items];\n    if (this.isConnected) {\n      this.render();\n    }\n  }\n\n  get value(): string[] {\n    return this.isConnected ? this.#read() : [...this.#items];\n  }\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.root.addEventListener('input', (event) => {\n      event.stopPropagation();\n      this.#changed();\n    });\n  }\n\n  // Entered text survives a language change.\n  override languageChanged(): void {\n    this.#rerender();\n  }\n\n  override handleAction(action: string, element: HTMLElement): void {\n    const items = this.#read();\n    const index = Number(element.dataset.index);\n    let focus: string | null = null;\n    if (action === 'add') {\n      items.push('');\n      focus = `[data-index=\"${items.length - 1}\"][data-field]`;\n    } else if (action === 'remove') {\n      items.splice(index, 1);\n    } else if ((action === 'up' && index > 0) || (action === 'down' && index < items.length - 1)) {\n      const target = action === 'up' ? index - 1 : index + 1;\n      [items[index], items[target]] = [items[target] ?? '', items[index] ?? ''];\n      focus = `[data-action=\"${action}\"][data-index=\"${target}\"]`;\n    } else {\n      return;\n    }\n    this.#items = items;\n    this.render();\n    if (focus !== null) {\n      this.$<HTMLElement>(focus)?.focus();\n    }\n    this.#changed();\n  }\n\n  #changed(): void {\n    this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n  }\n\n  #rerender(): void {\n    this.#items = this.#read();\n    this.render();\n  }\n\n  #read(): string[] {\n    const fields = this.$$<HTMLInputElement | HTMLTextAreaElement>('[data-field]');\n    return fields.length === 0 && this.$('[data-testid=\"add\"]') === null ? [...this.#items] : fields.map((field) => field.value);\n  }\n\n  override template(): RawHtml {\n    const l = t();\n    const multiline = this.hasAttribute('multiline');\n    const maxlength = this.getAttribute('maxlength') ?? '5000';\n    const last = this.#items.length - 1;\n    return html`\n      <fieldset class=\"vstack gap-2\">\n        <legend>${this.getAttribute('label') ?? ''}</legend>\n        <ol class=\"items unstyled vstack gap-2\" data-testid=\"items\">\n          ${this.#items.map((item, i) => {\n            const label = this.#itemLabel(i + 1);\n            return html`<li class=\"item\" data-testid=\"item\">\n              ${\n                multiline\n                  ? html`<textarea rows=\"3\" maxlength=\"${maxlength}\" aria-label=\"${label}\" data-field data-index=\"${i}\" data-testid=\"field-${i}\">\n${item}</textarea>`\n                  : html`<input\n                      type=\"text\"\n                      maxlength=\"${maxlength}\"\n                      aria-label=\"${label}\"\n                      value=\"${item}\"\n                      data-field\n                      data-index=\"${i}\"\n                      data-testid=\"field-${i}\"\n                    />`\n              }\n              <div class=\"buttons hstack gap-1\">\n                <button type=\"button\" class=\"outline icon\" data-action=\"up\" data-index=\"${i}\" aria-label=\"${l.up}: ${label}\" ${i === 0 ? 'disabled' : ''}>\n                  ↑\n                </button>\n                <button\n                  type=\"button\"\n                  class=\"outline icon\"\n                  data-action=\"down\"\n                  data-index=\"${i}\"\n                  aria-label=\"${l.down}: ${label}\"\n                  ${i === last ? 'disabled' : ''}\n                >\n                  ↓\n                </button>\n                <button\n                  type=\"button\"\n                  class=\"outline icon\"\n                  data-variant=\"danger\"\n                  data-action=\"remove\"\n                  data-index=\"${i}\"\n                  aria-label=\"${l.recipe.form.remove}: ${label}\"\n                >\n                  ×\n                </button>\n              </div>\n            </li>`;\n          })}\n        </ol>\n        <div><button type=\"button\" class=\"outline\" data-action=\"add\" data-testid=\"add\">+ ${this.getAttribute('add-label') ?? ''}</button></div>\n      </fieldset>\n    `;\n  }\n}\n\nawait define('ce-list-field', CeListFieldComponent, \"/features/recipes/internal/ce-list-field.component.ts\");\n",
    "import type { CreateRecipeDto, NutritionDto, RecipeDto } from '../../../shared/dto/recipe.ts';\nimport type { RecipeId } from '../../../shared/flavors.ts';\nimport type { Language } from '../../i18n/language.ts';\nimport type { RawHtml } from '../../ui/html.ts';\nimport { navigate } from '../../app/router.ts';\nimport { define } from '../../ui/base.ts';\nimport { html } from '../../ui/html.ts';\nimport { t } from '../../ui/i18n.ts';\nimport { guardLeave } from '../../ui/leave-guard.ts';\nimport { toast, toastError } from '../../ui/toast.ts';\nimport { CeView } from '../../ui/view.ts';\nimport { tagFacade } from '../tags/tags.facade.ts';\nimport type { CeHistoryFieldComponent } from './internal/ce-history-field.component.ts';\nimport './internal/ce-history-field.component.ts';\nimport type { CeListFieldComponent } from './internal/ce-list-field.component.ts';\nimport './internal/ce-list-field.component.ts';\nimport { PLACEHOLDER_THUMBNAIL } from './internal/ce-recipe-card.component.ts';\nimport { recipeFacade } from './recipes.facade.ts';\n\nexport const MAX_IMAGE_BYTES = 10 * 1024 * 1024;\n\nconst NUTRIENTS: readonly (keyof NutritionDto)[] = [\n  'calories',\n  'carbohydrate',\n  'cholesterol',\n  'fat',\n  'fiber',\n  'protein',\n  'saturatedFat',\n  'sodium',\n  'sugar',\n  'transFat',\n  'unsaturatedFat',\n];\n\ninterface EditData {\n  recipe: RecipeDto | null;\n  /** Every tag's title, offered while typing. */\n  tagTitles: string[];\n}\n\n/** A tag Oat's taginput keeps whole: its `value` setter splits strings on commas, but not objects. */\ninterface TagValue {\n  title: string;\n  toString: () => string;\n}\n\nfunction tagValue(title: string): TagValue {\n  return { title, toString: () => title };\n}\n\nfunction tagTitle(value: unknown): string {\n  if (typeof value === 'string') {\n    return value.trim();\n  }\n  return typeof value === 'object' && value !== null && 'title' in value && typeof value.title === 'string' ? value.title.trim() : '';\n}\n\nfunction numberOrNull(value: string | undefined): number | null {\n  const trimmed = value?.trim() ?? '';\n  return trimmed === '' || Number.isNaN(Number(trimmed)) ? null : Number(trimmed);\n}\n\n/** `/recipes/new` without a `recipe-id`, `/recipes/:id/edit` with one. */\nexport class CeRecipeEditComponent extends CeView<EditData> {\n  #image: File | null = null;\n  #removeImage = false;\n  #preview: string | null = null;\n  #snapshot = '';\n  #savedId: RecipeId | null = null;\n  #saving = false;\n  #releaseGuard: (() => void) | null = null;\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.#releaseGuard = guardLeave(() => this.isDirty());\n    this.root.addEventListener('change', (event) => {\n      if (event.target instanceof HTMLInputElement && event.target.type === 'file') {\n        this.#pick(event.target);\n      }\n    });\n  }\n\n  override disconnectedCallback(): void {\n    super.disconnectedCallback();\n    this.#releaseGuard?.();\n    this.#releaseGuard = null;\n    this.#revokePreview();\n  }\n\n  // A language change would re-render the form and lose what was typed.\n  override languageChanged(): void {\n    if (!this.isDirty()) {\n      super.languageChanged();\n    }\n  }\n\n  override async load(): Promise<EditData> {\n    const id = this.hasAttribute('recipe-id') ? this.numericAttribute('recipe-id') : null;\n    const [recipe, tags] = await Promise.all([id === null ? Promise.resolve(null) : recipeFacade.get(id), tagFacade.list()]);\n    return { recipe, tagTitles: tags.map((tag) => tag.title) };\n  }\n\n  override titleFor({ recipe }: EditData): string {\n    return recipe === null ? t().recipe.newRecipe : t().edit;\n  }\n\n  override parentsFor({ recipe }: EditData): { path: string; label: string }[] | null {\n    return recipe === null\n      ? null\n      : [\n          { path: '/', label: t().recipes },\n          { path: `/recipes/${recipe.id}`, label: recipe.title },\n        ];\n  }\n\n  isDirty(): boolean {\n    const form = this.$<HTMLFormElement>('form');\n    if (!form || this.#saving) {\n      return false;\n    }\n    return this.#image !== null || this.#removeImage || JSON.stringify(this.#collect(form)) !== this.#snapshot;\n  }\n\n  override afterRender(): void {\n    const data = this.data;\n    const form = this.$<HTMLFormElement>('form');\n    if (!data || !form) {\n      return;\n    }\n    const { recipe } = data;\n    const l = t();\n    const ingredients = this.$<CeListFieldComponent>('[data-testid=\"ingredients\"]');\n    if (ingredients) {\n      ingredients.itemLabel = l.recipe.form.ingredient;\n      ingredients.value = recipe?.ingredients.map((ingredient) => ingredient.raw) ?? [];\n    }\n    const instructions = this.$<CeListFieldComponent>('[data-testid=\"instructions\"]');\n    if (instructions) {\n      instructions.itemLabel = l.recipe.form.instruction;\n      instructions.value = recipe?.instructions ?? [];\n    }\n    const history = this.$<CeHistoryFieldComponent>('[data-testid=\"history\"]');\n    if (history) {\n      history.value = recipe?.history ?? [];\n    }\n    this.#setTags(recipe?.tags.map((tag) => tag.title) ?? []);\n    this.#snapshot = JSON.stringify(this.#collect(form));\n  }\n\n  // Set once Oat defined the element: a property set before the upgrade would shadow its accessor.\n  #setTags(titles: string[]): void {\n    const input = this.$('ot-taginput');\n    if (!input) {\n      return;\n    }\n    const apply = (): void => {\n      Reflect.set(input, 'value', titles.map(tagValue));\n      const form = this.$<HTMLFormElement>('form');\n      if (form) {\n        this.#snapshot = JSON.stringify(this.#collect(form));\n      }\n    };\n    if (customElements.get('ot-taginput')) {\n      apply();\n    } else {\n      void customElements.whenDefined('ot-taginput').then(apply);\n    }\n  }\n\n  #tags(): string[] {\n    const input = this.$('ot-taginput');\n    const value: unknown = input ? Reflect.get(input, 'value') : [];\n    return Array.isArray(value) ? value.map(tagTitle).filter((title) => title !== '') : [];\n  }\n\n  #pick(input: HTMLInputElement): void {\n    const file = input.files?.[0];\n    if (!file) {\n      return;\n    }\n    const l = t();\n    if (!file.type.startsWith('image/')) {\n      toast(l.recipe.form.imageError, 'error');\n      input.value = '';\n      return;\n    }\n    if (file.size > MAX_IMAGE_BYTES) {\n      toast(l.recipe.form.imageTooLarge, 'error');\n      input.value = '';\n      return;\n    }\n    this.#image = file;\n    this.#removeImage = false;\n    this.#revokePreview();\n    this.#preview = URL.createObjectURL(file);\n    this.#showPreview(this.#preview);\n  }\n\n  #revokePreview(): void {\n    if (this.#preview !== null) {\n      URL.revokeObjectURL(this.#preview);\n      this.#preview = null;\n    }\n  }\n\n  #showPreview(src: string | null): void {\n    const image = this.$<HTMLImageElement>('[data-testid=\"preview\"]');\n    if (image) {\n      image.src = src ?? PLACEHOLDER_THUMBNAIL;\n    }\n    this.$<HTMLButtonElement>('[data-testid=\"remove-thumbnail\"]')?.toggleAttribute('disabled', src === null);\n  }\n\n  override handleAction(action: string): void {\n    if (action === 'remove-thumbnail') {\n      this.#image = null;\n      this.#removeImage = (this.data?.recipe?.thumbnail ?? null) !== null;\n      this.#revokePreview();\n      const input = this.$<HTMLInputElement>('input[type=\"file\"]');\n      if (input) {\n        input.value = '';\n      }\n      this.#showPreview(null);\n    }\n  }\n\n  #collect(form: HTMLFormElement): CreateRecipeDto {\n    const values = this.formData(form);\n    const nutrition: Partial<NutritionDto> = {};\n    for (const key of NUTRIENTS) {\n      const value = values[`nutrition.${key}`] ?? '';\n      nutrition[key] = value === '' ? null : value;\n    }\n    return {\n      title: values.title ?? '',\n      flagged: this.data?.recipe?.flagged ?? false,\n      description: values.description ?? '',\n      source: values.source ?? '',\n      yield: numberOrNull(values.yield),\n      nutrition,\n      prepTime: numberOrNull(values.prepTime) ?? 0,\n      cookTime: numberOrNull(values.cookTime) ?? 0,\n      aggregateRating: { value: numberOrNull(values.aggregateRatingValue), count: numberOrNull(values.aggregateRatingCount) },\n      rating: numberOrNull(values.rating) ?? 0,\n      ingredients:\n        this.$<CeListFieldComponent>('[data-testid=\"ingredients\"]')\n          ?.value.map((line) => line.trim())\n          .filter((line) => line !== '') ?? [],\n      instructions:\n        this.$<CeListFieldComponent>('[data-testid=\"instructions\"]')\n          ?.value.map((line) => line.trim())\n          .filter((line) => line !== '') ?? [],\n      history: this.$<CeHistoryFieldComponent>('[data-testid=\"history\"]')?.value ?? [],\n      tags: this.#tags(),\n    };\n  }\n\n  override async handleSubmit(action: string, form: HTMLFormElement): Promise<void> {\n    if (action !== 'save' || this.#saving) {\n      return;\n    }\n    const dto = this.#collect(form);\n    const button = this.$('[data-testid=\"save\"]');\n    button?.setAttribute('aria-busy', 'true');\n    this.#saving = true;\n    let saved: RecipeDto;\n    try {\n      const id = this.data?.recipe?.id ?? this.#savedId;\n      saved = id === null ? await recipeFacade.create(dto) : await recipeFacade.update(id, dto);\n      // A retry after a failed upload must not create the recipe twice.\n      this.#savedId = saved.id;\n      if (this.#image !== null) {\n        saved = await recipeFacade.putThumbnail(saved.id, this.#image);\n      } else if (this.#removeImage && saved.thumbnail !== null) {\n        saved = await recipeFacade.deleteThumbnail(saved.id);\n      }\n    } catch (error) {\n      this.#saving = false;\n      button?.removeAttribute('aria-busy');\n      toastError(error);\n      return;\n    }\n    this.#releaseGuard?.();\n    this.#releaseGuard = null;\n    toast(t().recipe.form.saved, 'success');\n    navigate(`/recipes/${saved.id}`);\n  }\n\n  #nutritionFields(l: Language, nutrition: NutritionDto | undefined): RawHtml {\n    return html`${NUTRIENTS.map(\n      (key) =>\n        html`<div class=\"field\">\n          <label for=\"nutrition-${key}\">${l.recipe.nutrition[key]}</label>\n          <input id=\"nutrition-${key}\" name=\"nutrition.${key}\" type=\"text\" maxlength=\"200\" value=\"${nutrition?.[key] ?? ''}\" />\n        </div>`,\n    )}`;\n  }\n\n  override readyTemplate({ recipe, tagTitles }: EditData): RawHtml {\n    const l = t();\n    const f = l.recipe.form;\n    const thumbnail = recipe?.thumbnail ?? null;\n    return html`\n      <form class=\"vstack gap-4\" data-action=\"save\" data-testid=\"form\">\n        <h1>${recipe === null ? l.recipe.newRecipe : recipe.title}</h1>\n        <div class=\"columns\">\n          <section class=\"card vstack gap-3\">\n            <h2>${f.group.basic}</h2>\n            <div class=\"field\">\n              <label for=\"title\">${l.title} *</label>\n              <input id=\"title\" name=\"title\" type=\"text\" required maxlength=\"500\" value=\"${recipe?.title ?? ''}\" data-testid=\"title\" />\n            </div>\n            <div class=\"field\">\n              <label for=\"description\">${l.description}</label>\n              <textarea id=\"description\" name=\"description\" rows=\"3\" maxlength=\"50000\" data-testid=\"description\">${recipe?.description ?? ''}</textarea>\n            </div>\n            <div class=\"field\">\n              <label for=\"source\">${f.sourceUrl}</label>\n              <input id=\"source\" name=\"source\" type=\"text\" inputmode=\"url\" maxlength=\"2000\" value=\"${recipe?.source ?? ''}\" data-testid=\"source\" />\n            </div>\n            <div class=\"field\">\n              <label for=\"yield\">${l.recipe.yield}</label>\n              <input id=\"yield\" name=\"yield\" type=\"number\" min=\"1\" max=\"99\" step=\"any\" value=\"${recipe?.yield ?? ''}\" data-testid=\"yield\" />\n            </div>\n            <div class=\"field\">\n              <label for=\"tags\">${l.navigation.tags}</label>\n              <ot-taginput data-testid=\"tags\">\n                <input id=\"tags\" list=\"tag-options\" placeholder=\"${f.tagsPlaceholder}\" maxlength=\"200\" autocomplete=\"off\" />\n              </ot-taginput>\n              <datalist id=\"tag-options\">${tagTitles.map((title) => html`<option value=\"${title}\"></option>`)}</datalist>\n            </div>\n          </section>\n          <section class=\"card vstack gap-3\">\n            <h2>${f.thumbnail}</h2>\n            <img class=\"preview\" src=\"${thumbnail ?? PLACEHOLDER_THUMBNAIL}\" alt=\"\" data-testid=\"preview\" />\n            <ot-upload>\n              <input type=\"file\" accept=\"image/*\" hidden data-testid=\"file\" />\n              <small data-hint>${f.thumbnailHint}</small>\n            </ot-upload>\n            <div>\n              <button type=\"button\" class=\"outline\" data-action=\"remove-thumbnail\" data-testid=\"remove-thumbnail\" ${thumbnail === null ? 'disabled' : ''}>\n                ${f.removeThumbnail}\n              </button>\n            </div>\n          </section>\n        </div>\n        <section class=\"card vstack gap-3\">\n          <h2>${f.group.nutrition}</h2>\n          <div class=\"nutrition\">${this.#nutritionFields(l, recipe?.nutrition)}</div>\n        </section>\n        <div class=\"columns\">\n          <section class=\"card vstack gap-3\">\n            <h2>${f.group.times}</h2>\n            <div class=\"fields\">\n              <div class=\"field\">\n                <label for=\"prepTime\">${l.recipe.prepTime}</label>\n                <input id=\"prepTime\" name=\"prepTime\" type=\"number\" min=\"0\" max=\"1440\" step=\"any\" value=\"${recipe?.prepTime ?? ''}\" data-testid=\"prepTime\" />\n              </div>\n              <div class=\"field\">\n                <label for=\"cookTime\">${l.recipe.cookTime}</label>\n                <input id=\"cookTime\" name=\"cookTime\" type=\"number\" min=\"0\" max=\"1440\" step=\"any\" value=\"${recipe?.cookTime ?? ''}\" data-testid=\"cookTime\" />\n              </div>\n            </div>\n            <small class=\"text-light\">${f.timeHint}</small>\n          </section>\n          <section class=\"card vstack gap-3\">\n            <h2>${f.group.ratings}</h2>\n            <div class=\"fields\">\n              <div class=\"field\">\n                <label for=\"aggregateRatingValue\">${l.recipe.aggregateRatingValue}</label>\n                <input\n                  id=\"aggregateRatingValue\"\n                  name=\"aggregateRatingValue\"\n                  type=\"number\"\n                  min=\"0\"\n                  step=\"any\"\n                  value=\"${recipe?.aggregateRating.value ?? ''}\"\n                  data-testid=\"aggregateRatingValue\"\n                />\n              </div>\n              <div class=\"field\">\n                <label for=\"aggregateRatingCount\">${l.recipe.aggregateRatingCount}</label>\n                <input\n                  id=\"aggregateRatingCount\"\n                  name=\"aggregateRatingCount\"\n                  type=\"number\"\n                  min=\"0\"\n                  step=\"any\"\n                  value=\"${recipe?.aggregateRating.count ?? ''}\"\n                  data-testid=\"aggregateRatingCount\"\n                />\n              </div>\n              <div class=\"field\">\n                <label for=\"rating\">${l.recipe.rating}</label>\n                <input id=\"rating\" name=\"rating\" type=\"number\" min=\"0\" max=\"5\" step=\"0.5\" value=\"${recipe?.rating ?? ''}\" data-testid=\"rating\" />\n              </div>\n            </div>\n          </section>\n        </div>\n        <section class=\"card\">\n          <ce-history-field label=\"${l.recipe.history}\" data-testid=\"history\"></ce-history-field>\n        </section>\n        <section class=\"card\">\n          <ce-list-field label=\"${l.recipe.ingredients.title}\" add-label=\"${f.createIngredient}\" maxlength=\"5000\" data-testid=\"ingredients\"></ce-list-field>\n        </section>\n        <section class=\"card\">\n          <ce-list-field\n            label=\"${l.recipe.instructions}\"\n            add-label=\"${f.createInstruction}\"\n            maxlength=\"50000\"\n            multiline\n            data-testid=\"instructions\"\n          ></ce-list-field>\n        </section>\n        <div class=\"actions hstack gap-2\">\n          <a class=\"button outline\" href=\"${recipe === null ? '/' : `/recipes/${recipe.id}`}\" data-testid=\"cancel\">${l.cancel}</a>\n          <button type=\"submit\" data-testid=\"save\">${l.save}</button>\n        </div>\n      </form>\n    `;\n  }\n}\n\nawait define('ce-recipe-edit', CeRecipeEditComponent, \"/features/recipes/ce-recipe-edit.component.ts\");\n",
    "import type { ImportResultItemDto } from '../../../shared/dto/recipe.ts';\nimport type { RawHtml } from '../../ui/html.ts';\nimport { define, CeElement } from '../../ui/base.ts';\nimport { html } from '../../ui/html.ts';\nimport { t } from '../../ui/i18n.ts';\nimport { toast, toastError } from '../../ui/toast.ts';\nimport { recipeFacade } from './recipes.facade.ts';\n\n/** Not a CeView: it has nothing to load and shows its form at once. */\nexport class CeRecipeImportComponent extends CeElement {\n  #results: ImportResultItemDto[] | null = null;\n  #busy = false;\n\n  // The typed URLs survive a language change.\n  override languageChanged(): void {\n    const text = this.$<HTMLTextAreaElement>('textarea')?.value ?? '';\n    this.render();\n    const textarea = this.$<HTMLTextAreaElement>('textarea');\n    if (textarea) {\n      textarea.value = text;\n    }\n  }\n\n  override async handleSubmit(action: string, form: HTMLFormElement): Promise<void> {\n    if (action !== 'import' || this.#busy) {\n      return;\n    }\n    const urls = (this.formData(form).urls ?? '')\n      .split(/\\r?\\n/)\n      .map((line) => line.trim())\n      .filter((line) => line !== '');\n    if (urls.length === 0) {\n      return;\n    }\n    this.#busy = true;\n    const button = this.$('[data-testid=\"submit\"]');\n    button?.setAttribute('aria-busy', 'true');\n    button?.setAttribute('disabled', '');\n    try {\n      this.#results = (await recipeFacade.import({ urls })).results;\n    } catch (error) {\n      toastError(error);\n      return;\n    } finally {\n      this.#busy = false;\n      button?.removeAttribute('aria-busy');\n      button?.removeAttribute('disabled');\n    }\n    toast(t().recipe.import.alert, 'success');\n    const results = this.$('[data-testid=\"results\"]');\n    if (results) {\n      results.innerHTML = String(this.#table(this.#results));\n    }\n  }\n\n  #table(results: ImportResultItemDto[] | null): RawHtml {\n    if (results === null) {\n      return html``;\n    }\n    const l = t();\n    return html`\n      <div class=\"table\">\n        <table>\n          <thead>\n            <tr>\n              <th class=\"num\">#</th>\n              <th>${l.recipe.import.sourceUrl}</th>\n              <th>${l.recipe.import.result}</th>\n            </tr>\n          </thead>\n          <tbody>\n            ${results.map(\n              (result, i) =>\n                html`<tr data-testid=\"result\">\n                  <td class=\"num\">${i + 1}</td>\n                  <td class=\"url\"><a href=\"${result.url}\" target=\"_blank\" rel=\"noopener noreferrer\">${result.url} ↗</a></td>\n                  <td>\n                    ${\n                      result.recipe === null\n                        ? html`<span class=\"failed\" data-testid=\"error\">✗ ${result.error ?? ''}</span>`\n                        : html`<span class=\"succeeded\">✓</span>\n                            <a class=\"button outline\" href=\"/recipes/${result.recipe.id}\" data-testid=\"open\">${l.recipe.open}</a>\n                            <span class=\"text-light\">${result.recipe.title}</span>`\n                    }\n                  </td>\n                </tr>`,\n            )}\n          </tbody>\n        </table>\n      </div>\n    `;\n  }\n\n  override template(): RawHtml {\n    const l = t();\n    return html`\n      <div class=\"vstack gap-4\">\n        <h1>${l.recipe.import.title}</h1>\n        <form class=\"card vstack gap-3\" data-action=\"import\" data-testid=\"form\">\n          <div class=\"field\">\n            <label for=\"urls\">${l.recipe.import.urls}</label>\n            <textarea id=\"urls\" name=\"urls\" rows=\"10\" required placeholder=\"https://\" data-testid=\"urls\"></textarea>\n            <small class=\"text-light\">${l.recipe.import.urlInfo}</small>\n          </div>\n          <div><button type=\"submit\" data-testid=\"submit\">${l.recipe.import.title}</button></div>\n        </form>\n        <div data-testid=\"results\">${this.#table(this.#results)}</div>\n      </div>\n    `;\n  }\n}\n\nawait define('ce-recipe-import', CeRecipeImportComponent, \"/features/recipes/ce-recipe-import.component.ts\");\n",
    "import type { IngredientDto, QuantityDto } from '../../../../shared/dto/recipe.ts';\n\nexport const MIN_PORTIONS = 1;\nexport const MAX_PORTIONS = 99;\n\nexport function roundUpToThreeDigits(n: number): number {\n  return Math.round((n + Number.EPSILON) * 1000) / 1000;\n}\n\n/** A recipe without a yield scales as if it served one. */\nexport function scaleQuantity(quantity: QuantityDto, recipeYield: number | null, portions: number): QuantityDto {\n  const factor = Math.max(MIN_PORTIONS, portions) / (recipeYield !== null && recipeYield > 0 ? recipeYield : 1);\n  if (typeof quantity === 'number') {\n    return roundUpToThreeDigits(quantity * factor);\n  }\n  return { from: roundUpToThreeDigits(quantity.from * factor), to: roundUpToThreeDigits(quantity.to * factor) };\n}\n\n/** `?portions=` when it is a whole number in range, else the yield rounded into range. */\nexport function initialPortions(search: string, recipeYield: number | null): number {\n  const raw = new URLSearchParams(search).get('portions') ?? '';\n  if (/^\\d+$/.test(raw)) {\n    return clampPortions(Number(raw));\n  }\n  return clampPortions(Math.round(recipeYield ?? 1));\n}\n\nexport function clampPortions(portions: number): number {\n  return Math.min(MAX_PORTIONS, Math.max(MIN_PORTIONS, Math.round(portions)));\n}\n\n// Quantity and unit first, then a quantity alone, then neither; then by unit where both lines have one, as the old detail page did.\nfunction rank(ingredient: IngredientDto): number {\n  if (ingredient.quantity === null) {\n    return 3;\n  }\n  return ingredient.unit === null ? 2 : 1;\n}\n\nexport function sortIngredients(ingredients: readonly IngredientDto[]): IngredientDto[] {\n  return ingredients.toSorted((a, b) => rank(a) - rank(b)).toSorted((a, b) => (a.unit !== null && b.unit !== null ? a.unit.localeCompare(b.unit) : 0));\n}\n",
    "import type { IngredientDto, QuantityDto } from '../../../../shared/dto/recipe.ts';\nimport type { RawHtml } from '../../../ui/html.ts';\nimport { define, CeElement } from '../../../ui/base.ts';\nimport { formatDecimal } from '../../../ui/format.ts';\nimport { html } from '../../../ui/html.ts';\nimport { t } from '../../../ui/i18n.ts';\nimport { clampPortions, initialPortions, MAX_PORTIONS, MIN_PORTIONS, scaleQuantity, sortIngredients } from './portions.ts';\n\nfunction formatQuantity(quantity: QuantityDto): string {\n  return typeof quantity === 'number' ? formatDecimal(quantity) : `${formatDecimal(quantity.from)}–${formatDecimal(quantity.to)}`;\n}\n\n/** Scales locally: changing the portions sends no request and only rewrites `?portions=`. */\nexport class CeIngredientsComponent extends CeElement {\n  #ingredients: IngredientDto[] = [];\n  #yield: number | null = null;\n  #portions = 1;\n\n  set recipe(value: { ingredients: IngredientDto[]; yield: number | null }) {\n    this.#ingredients = sortIngredients(value.ingredients);\n    this.#yield = value.yield;\n    this.#portions = initialPortions(location.search, value.yield);\n    if (this.isConnected) {\n      this.render();\n    }\n  }\n\n  get portions(): number {\n    return this.#portions;\n  }\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.root.addEventListener('change', (event) => {\n      if (event.target instanceof HTMLInputElement && event.target.name === 'portions') {\n        this.#setPortions(Number(event.target.value));\n      }\n    });\n  }\n\n  override handleAction(action: string): void {\n    if (action === 'more') {\n      this.#setPortions(this.#portions + 1);\n    } else if (action === 'less') {\n      this.#setPortions(this.#portions - 1);\n    }\n  }\n\n  #setPortions(value: number): void {\n    this.#portions = Number.isFinite(value) ? clampPortions(value) : this.#portions;\n    const url = new URL(location.href);\n    url.searchParams.set('portions', String(this.#portions));\n    history.replaceState(history.state, '', url.pathname + url.search + url.hash);\n    // Patched rather than re-rendered, so the number field keeps focus.\n    const input = this.$<HTMLInputElement>('input[name=\"portions\"]');\n    if (input) {\n      input.value = String(this.#portions);\n    }\n    this.$<HTMLButtonElement>('[data-action=\"less\"]')?.toggleAttribute('disabled', this.#portions <= MIN_PORTIONS);\n    this.$<HTMLButtonElement>('[data-action=\"more\"]')?.toggleAttribute('disabled', this.#portions >= MAX_PORTIONS);\n    const list = this.$('[data-testid=\"list\"]');\n    if (list) {\n      list.innerHTML = String(this.#rows());\n    }\n  }\n\n  #rows(): RawHtml {\n    return html`${this.#ingredients.map((ingredient) => {\n      const quantity = ingredient.quantity === null ? '' : formatQuantity(scaleQuantity(ingredient.quantity, this.#yield, this.#portions));\n      const amount = [quantity, ingredient.quantity === null ? '' : (ingredient.unit ?? '')].filter((part) => part !== '').join(' ');\n      return html`<tr data-testid=\"ingredient\">\n        <td class=\"amount nowrap\" data-testid=\"amount\">${amount}</td>\n        <td data-testid=\"description\">${ingredient.quantity === null ? ingredient.raw : ingredient.description}</td>\n      </tr>`;\n    })}`;\n  }\n\n  override template(): RawHtml {\n    const l = t();\n    return html`\n      <section class=\"card vstack gap-3\">\n        <header class=\"hstack justify-between gap-2\">\n          <h2>${l.recipe.ingredients.title}</h2>\n          <div class=\"portions hstack gap-1\" role=\"group\" aria-label=\"${l.recipe.portions}\">\n            <label for=\"portions\">${l.recipe.portions}</label>\n            <button type=\"button\" class=\"outline icon\" data-action=\"less\" aria-label=\"−\" data-testid=\"less\" ${this.#portions <= MIN_PORTIONS ? 'disabled' : ''}>\n              −\n            </button>\n            <input\n              id=\"portions\"\n              name=\"portions\"\n              type=\"number\"\n              inputmode=\"numeric\"\n              min=\"${MIN_PORTIONS}\"\n              max=\"${MAX_PORTIONS}\"\n              step=\"1\"\n              value=\"${this.#portions}\"\n              data-testid=\"portions\"\n            />\n            <button type=\"button\" class=\"outline icon\" data-action=\"more\" aria-label=\"+\" data-testid=\"more\" ${this.#portions >= MAX_PORTIONS ? 'disabled' : ''}>\n              +\n            </button>\n          </div>\n        </header>\n        <div class=\"table\">\n          <table>\n            <tbody data-testid=\"list\">\n              ${this.#rows()}\n            </tbody>\n          </table>\n        </div>\n      </section>\n    `;\n  }\n}\n\nawait define('ce-ingredients', CeIngredientsComponent, \"/features/recipes/internal/ce-ingredients.component.ts\");\n",
    "import type { RawHtml } from '../../../ui/html.ts';\nimport { define, CeElement } from '../../../ui/base.ts';\nimport { html } from '../../../ui/html.ts';\n\nconst STAR = 'M12 2.5l2.94 6.1 6.56.9-4.8 4.6 1.18 6.6L12 17.5l-5.88 3.2 1.18-6.6-4.8-4.6 6.56-.9z';\n\n/** Ten half-star radios; `value` rounds to the nearest half. Emits `rating-change` with the chosen value unless `readonly`. */\nexport class CeRatingComponent extends CeElement {\n  static observedAttributes = ['value', 'readonly', 'label'];\n\n  attributeChangedCallback(): void {\n    if (this.isConnected) {\n      this.render();\n    }\n  }\n\n  get value(): number {\n    return Math.round(Number(this.getAttribute('value') ?? 0) * 2) / 2;\n  }\n\n  set value(value: number) {\n    this.setAttribute('value', String(value));\n  }\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.root.addEventListener('change', (event) => {\n      if (event.target instanceof HTMLInputElement && !this.hasAttribute('readonly')) {\n        this.emit('rating-change', Number(event.target.value));\n      }\n    });\n  }\n\n  override template(): RawHtml {\n    const readonly = this.hasAttribute('readonly');\n    const value = this.value;\n    // Highest first, so `:checked ~ label` and `label:hover ~ label` reach the lower stars in a row-reverse.\n    const steps = Array.from({ length: 10 }, (_, i) => (10 - i) / 2);\n    return html`\n      <div class=\"stars${readonly ? ' readonly' : ''}\" role=\"radiogroup\" aria-label=\"${this.getAttribute('label') ?? ''}\" data-testid=\"stars\">\n        ${steps.map(\n          (step) => html`\n            <input\n              type=\"radio\"\n              id=\"star-${step * 2}\"\n              name=\"rating\"\n              value=\"${step}\"\n              aria-label=\"${step}\"\n              ${step === value ? 'checked' : ''}\n              ${readonly ? 'disabled' : ''}\n              data-testid=\"star-${step * 2}\"\n            />\n            <label for=\"star-${step * 2}\" class=\"${step % 1 === 0 ? 'full' : 'half'}\" title=\"${readonly ? '' : String(step)}\">\n              <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"${STAR}\" /></svg>\n            </label>\n          `,\n        )}\n      </div>\n    `;\n  }\n}\n\nawait define('ce-rating', CeRatingComponent, \"/features/recipes/internal/ce-rating.component.ts\");\n",
    "interface Sentinel {\n  release: () => Promise<void>;\n}\n\ninterface WakeLockApi {\n  request: (type: 'screen') => Promise<Sentinel>;\n}\n\nfunction isWakeLockApi(value: unknown): value is WakeLockApi {\n  return typeof value === 'object' && value !== null && 'request' in value && typeof value.request === 'function';\n}\n\n// Not in every browser, and absent from happy-dom.\nfunction wakeLockApi(): WakeLockApi | null {\n  const api: unknown = Reflect.get(navigator, 'wakeLock');\n  return isWakeLockApi(api) ? api : null;\n}\n\n/** Keeps the screen on while cooking: held while acquired, taken again when the page returns to view, as the browser drops it when hidden. */\nexport class ScreenWakeLock {\n  #sentinel: Sentinel | null = null;\n  #held = false;\n\n  readonly #onVisibility = (): void => {\n    if (this.#held && document.visibilityState === 'visible') {\n      void this.#request();\n    }\n  };\n\n  acquire(): void {\n    if (this.#held) {\n      return;\n    }\n    this.#held = true;\n    document.addEventListener('visibilitychange', this.#onVisibility);\n    void this.#request();\n  }\n\n  release(): void {\n    this.#held = false;\n    document.removeEventListener('visibilitychange', this.#onVisibility);\n    const sentinel = this.#sentinel;\n    this.#sentinel = null;\n    void sentinel?.release().catch(() => {});\n  }\n\n  async #request(): Promise<void> {\n    const api = wakeLockApi();\n    if (!api) {\n      return;\n    }\n    try {\n      const sentinel = await api.request('screen');\n      if (this.#held) {\n        this.#sentinel = sentinel;\n      } else {\n        await sentinel.release();\n      }\n    } catch {\n      // Refused, e.g. on battery saver or in a hidden tab; the page works without it.\n    }\n  }\n}\n",
    "import type { NutritionDto, RecipeDto } from '../../../shared/dto/recipe.ts';\nimport type { RawHtml } from '../../ui/html.ts';\nimport { navigate } from '../../app/router.ts';\nimport { define } from '../../ui/base.ts';\nimport { confirmAction } from '../../ui/confirm/confirm.ts';\nimport { formatDate, formatMinutes, formatNumber, formatRelative } from '../../ui/format.ts';\nimport { html } from '../../ui/html.ts';\nimport { t } from '../../ui/i18n.ts';\nimport { toast, toastError } from '../../ui/toast.ts';\nimport { CeView } from '../../ui/view.ts';\nimport type { CeIngredientsComponent } from './internal/ce-ingredients.component.ts';\nimport './internal/ce-ingredients.component.ts';\nimport './internal/ce-rating.component.ts';\nimport { ScreenWakeLock } from './internal/wake-lock.ts';\nimport { recipeFacade } from './recipes.facade.ts';\n\nconst NUTRITION_ORDER: readonly (keyof NutritionDto)[] = [\n  'calories',\n  'fiber',\n  'fat',\n  'saturatedFat',\n  'unsaturatedFat',\n  'transFat',\n  'carbohydrate',\n  'sugar',\n  'protein',\n  'cholesterol',\n  'sodium',\n];\n\n/** A source is linked only when it is an http(s) URL; anything else an import or an old row stored shows as text. */\nexport function sourceUrl(source: string): URL | null {\n  try {\n    const url = new URL(source);\n    return url.protocol === 'http:' || url.protocol === 'https:' ? url : null;\n  } catch {\n    return null;\n  }\n}\n\nexport class CeRecipeDetailComponent extends CeView<RecipeDto> {\n  readonly #wakeLock = new ScreenWakeLock();\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.#wakeLock.acquire();\n    this.root.addEventListener('rating-change', (event) => {\n      if (event instanceof CustomEvent && typeof event.detail === 'number' && event.target instanceof HTMLElement && event.target.dataset.testid === 'rating') {\n        void this.#update({ rating: event.detail });\n      }\n    });\n  }\n\n  override disconnectedCallback(): void {\n    super.disconnectedCallback();\n    this.#wakeLock.release();\n  }\n\n  override load(): Promise<RecipeDto> {\n    return recipeFacade.get(this.numericAttribute('recipe-id'));\n  }\n\n  override titleFor(recipe: RecipeDto): string {\n    return recipe.title;\n  }\n\n  override async handleAction(action: string): Promise<void> {\n    const recipe = this.data;\n    if (!recipe) {\n      return;\n    }\n    if (action === 'flag') {\n      await this.#update({ flagged: !recipe.flagged });\n    } else if (action === 'delete') {\n      await this.#delete(recipe);\n    }\n  }\n\n  // Flag and rating patch the page in place, so the rest of it (portions typed, steps folded) stays as it is.\n  async #update(change: { flagged?: boolean; rating?: number }): Promise<void> {\n    const recipe = this.data;\n    if (!recipe) {\n      return;\n    }\n    let updated: RecipeDto;\n    try {\n      updated = await recipeFacade.update(recipe.id, change);\n    } catch (error) {\n      toastError(error);\n      this.#patch(recipe);\n      return;\n    }\n    this.replaceData(updated);\n    this.#patch(updated);\n  }\n\n  #patch(recipe: RecipeDto): void {\n    const l = t();\n    const flag = this.$('[data-testid=\"flag\"]');\n    if (flag) {\n      flag.textContent = recipe.flagged ? l.recipe.unflag : l.recipe.flag;\n      flag.setAttribute('aria-pressed', String(recipe.flagged));\n    }\n    this.$('[data-testid=\"flag-mark\"]')?.toggleAttribute('hidden', !recipe.flagged);\n    const rating = this.$('[data-testid=\"rating\"]');\n    rating?.setAttribute('value', String(recipe.rating));\n    const value = this.$('[data-testid=\"rating-value\"]');\n    if (value) {\n      value.textContent = this.#ratingText(recipe.rating);\n    }\n    const history = this.$('[data-testid=\"history\"]');\n    if (history) {\n      history.innerHTML = String(this.#history(recipe));\n    }\n  }\n\n  async #delete(recipe: RecipeDto): Promise<void> {\n    const l = t();\n    const confirmed = await confirmAction({\n      title: l.recipe.deleteTitle(recipe.title),\n      message: l.recipe.deleteConfirmation,\n      confirmLabel: l.delete,\n      danger: true,\n    });\n    if (!confirmed) {\n      return;\n    }\n    try {\n      await recipeFacade.delete(recipe.id);\n    } catch (error) {\n      toastError(error);\n      return;\n    }\n    toast(l.recipe.deleteSuccessful, 'success');\n    navigate('/');\n  }\n\n  #ratingText(rating: number): string {\n    return rating > 0 ? formatNumber(rating, 1) : t().recipe.noRating;\n  }\n\n  #history(recipe: RecipeDto): RawHtml {\n    if (recipe.history.length === 0) {\n      return html`<p class=\"text-light\">${t().recipe.notCookedYet}</p>`;\n    }\n    return html`<ul>\n      ${recipe.history.map((entry) => html`<li title=\"${formatDate(entry, { dateStyle: 'full', timeStyle: 'short' })}\">${formatRelative(entry)}</li>`)}\n    </ul>`;\n  }\n\n  #nutrition(recipe: RecipeDto): RawHtml {\n    const l = t();\n    const rows = NUTRITION_ORDER.flatMap((key) => {\n      const value = recipe.nutrition[key];\n      return value === null || value === ''\n        ? []\n        : [\n            html`<tr>\n              <th scope=\"row\">${l.recipe.nutrition[key]}</th>\n              <td>${value}</td>\n            </tr>`,\n          ];\n    });\n    if (rows.length === 0) {\n      return html``;\n    }\n    return html`\n      <section class=\"card\" data-testid=\"nutrition\">\n        <h3>${l.nutritionalValue}</h3>\n        <div class=\"table\">\n          <table>\n            <tbody>\n              ${rows}\n            </tbody>\n          </table>\n        </div>\n      </section>\n    `;\n  }\n\n  #times(recipe: RecipeDto): RawHtml {\n    const l = t();\n    const rows: [string, number][] = [\n      [l.recipe.time.prep, recipe.prepTime],\n      [l.recipe.time.cook, recipe.cookTime],\n      [l.recipe.time.total, recipe.totalTime],\n    ];\n    const shown = rows.filter(([, minutes]) => minutes > 0);\n    return html`\n      <section class=\"card\" data-testid=\"times\">\n        <h3>${l.recipe.form.group.times}</h3>\n        ${\n          shown.length === 0\n            ? html`<p class=\"text-light\">${l.recipe.noTimes}</p>`\n            : html`<div class=\"table\">\n                <table>\n                  <tbody>\n                    ${shown.map(\n                      ([label, minutes]) =>\n                        html`<tr>\n                          <th scope=\"row\">${label}</th>\n                          <td>${formatMinutes(minutes)}</td>\n                        </tr>`,\n                    )}\n                  </tbody>\n                </table>\n              </div>`\n        }\n      </section>\n    `;\n  }\n\n  #header(recipe: RecipeDto): RawHtml {\n    const l = t();\n    const source = sourceUrl(recipe.source);\n    const { value, count } = recipe.aggregateRating;\n    return html`\n      <header class=\"vstack gap-2\">\n        <div class=\"title hstack justify-between gap-2\">\n          <h1><span class=\"flag-mark\" data-testid=\"flag-mark\" ${recipe.flagged ? '' : 'hidden'}>⚑ </span><span data-testid=\"title\">${recipe.title}</span></h1>\n          <div class=\"hstack gap-2\">\n            <button type=\"button\" class=\"outline\" data-action=\"flag\" aria-pressed=\"${String(recipe.flagged)}\" data-testid=\"flag\">\n              ${recipe.flagged ? l.recipe.unflag : l.recipe.flag}\n            </button>\n            <a class=\"button outline\" href=\"/recipes/${recipe.id}/edit\" data-testid=\"edit\">${l.edit}</a>\n            <button type=\"button\" data-variant=\"danger\" data-action=\"delete\" data-testid=\"delete\">${l.delete}</button>\n          </div>\n        </div>\n        ${\n          source !== null || recipe.source !== '' || value !== null\n            ? html`<div class=\"meta hstack gap-3 text-light\">\n                ${\n                  source\n                    ? html`<a href=\"${source.href}\" target=\"_blank\" rel=\"noopener noreferrer\" data-testid=\"source\">${source.hostname} ↗</a>`\n                    : recipe.source === ''\n                      ? ''\n                      : html`<span data-testid=\"source-text\">${recipe.source}</span>`\n                }\n                ${\n                  value === null\n                    ? ''\n                    : html`<span class=\"hstack gap-1\" title=\"${l.recipe.aggregateRating}\" data-testid=\"aggregate-rating\">\n                        ${l.recipe.aggregateRating}\n                        <ce-rating readonly value=\"${Math.min(5, value)}\" label=\"${l.recipe.aggregateRating}\"></ce-rating>\n                        ${formatNumber(value)}${count === null ? '' : ` (${l.recipe.ratingCount(count)})`}\n                      </span>`\n                }\n              </div>`\n            : ''\n        }\n        ${\n          recipe.tags.length === 0\n            ? ''\n            : html`<div class=\"tags hstack gap-2\" data-testid=\"tags\">\n                ${recipe.tags.map((tag) => html`<a class=\"badge\" href=\"/?tag=${tag.id}\">${tag.title}</a>`)}\n              </div>`\n        }\n        ${recipe.description === '' ? '' : html`<p class=\"description\" data-testid=\"description\">${recipe.description}</p>`}\n      </header>\n    `;\n  }\n\n  override afterRender(): void {\n    const recipe = this.data;\n    const ingredients = this.$<CeIngredientsComponent>('ce-ingredients');\n    if (recipe && ingredients) {\n      ingredients.recipe = { ingredients: recipe.ingredients, yield: recipe.yield };\n    }\n  }\n\n  override readyTemplate(recipe: RecipeDto): RawHtml {\n    const l = t();\n    return html`\n      <article class=\"vstack gap-4\">\n        ${this.#header(recipe)}\n        <div class=\"top\">\n          ${recipe.thumbnail === null ? '' : html`<img class=\"thumbnail\" src=\"${recipe.thumbnail}\" alt=\"\" data-testid=\"thumbnail\" />`}\n          <section class=\"card your-rating\" data-testid=\"your-rating\">\n            <h3>${l.recipe.rating}</h3>\n            <div class=\"hstack gap-3\">\n              <ce-rating value=\"${recipe.rating}\" label=\"${l.recipe.rating}\" data-testid=\"rating\"></ce-rating>\n              <span class=\"text-light\" data-testid=\"rating-value\">${this.#ratingText(recipe.rating)}</span>\n            </div>\n          </section>\n        </div>\n        <div class=\"facts\">\n          ${this.#nutrition(recipe)} ${this.#times(recipe)}\n          <section class=\"card\">\n            <h3>${l.recipe.history}</h3>\n            <div data-testid=\"history\">${this.#history(recipe)}</div>\n          </section>\n        </div>\n        ${recipe.ingredients.length === 0 ? '' : html`<ce-ingredients data-testid=\"ingredients\"></ce-ingredients>`}\n        ${\n          recipe.instructions.length === 0\n            ? ''\n            : html`<section class=\"vstack gap-2\" data-testid=\"instructions\">\n                <h2>${l.recipe.instructions}</h2>\n                ${recipe.instructions.map(\n                  (instruction, i) =>\n                    html`<details open data-testid=\"step\">\n                      <summary>${l.recipe.ingredients.step(i + 1)}</summary>\n                      <p class=\"step\">${instruction}</p>\n                    </details>`,\n                )}\n              </section>`\n        }\n        ${\n          recipe.reviews.length === 0\n            ? ''\n            : html`<section class=\"vstack gap-2\" data-testid=\"reviews\">\n                <h2>${l.recipe.reviews}</h2>\n                ${recipe.reviews.map(\n                  (review) =>\n                    html`<blockquote class=\"review\">\n                      <p>${review.text}</p>\n                      <footer class=\"text-light\">${formatDate(review.date)}</footer>\n                    </blockquote>`,\n                )}\n              </section>`\n        }\n      </article>\n    `;\n  }\n}\n\nawait define('ce-recipe-detail', CeRecipeDetailComponent, \"/features/recipes/ce-recipe-detail.component.ts\");\n",
    "(()=>{var d=class extends HTMLElement{#t=!1;connectedCallback(){this.#t||(document.readyState===\"loading\"?document.addEventListener(\"DOMContentLoaded\",()=>this.#e(),{once:!0}):this.#e())}#e(){this.#t||(this.#t=!0,this.init())}disconnectedCallback(){this.cleanup()}cleanup(){}handleEvent(t){let e=this[`on${t.type}`];e&&e.call(this,t)}keyNav(t,e,s,n,o,a=!1){let{key:r}=t,l=-1;return r===o?l=(e+1)%s:r===n?l=(e-1+s)%s:a&&(r===\"Home\"?l=0:r===\"End\"&&(l=s-1)),l>=0&&t.preventDefault(),l}emit(t,e=null){return this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0,cancelable:!0,detail:e}))}uid(){return Math.random().toString(36).slice(2,10)}};\"commandForElement\"in HTMLButtonElement.prototype||document.addEventListener(\"click\",i=>{let t=i.target.closest(\"button[commandfor]\");if(!t)return;let e=document.getElementById(t.getAttribute(\"commandfor\"));if(!e)return;let s=t.getAttribute(\"command\")||\"toggle\";e instanceof HTMLDialogElement&&(s===\"show-modal\"?e.showModal():s===\"close\"||e.open?e.close():e.showModal())});document.addEventListener(\"touchstart\",i=>{i.target instanceof HTMLDialogElement&&(i.preventDefault(),i.target.close())},{passive:!1});var c=class extends d{#t=[];#e=[];#i;#s=[];init(){let t=this.querySelector(':scope > [role=\"tablist\"]');if(this.#t=t?[...t.querySelectorAll('[role=\"tab\"]')]:[],this.#e=[...this.querySelectorAll(':scope > [role=\"tabpanel\"]')],this.#t.length===0||this.#e.length===0){console.warn(\"ot-tabs: Missing tab or tabpanel elements\");return}this.#i=this.dataset.anchor,this.#t.forEach((n,o)=>{let a=this.#e[o];a&&(this.#s[o]=n.id||\"\",n.id||=`ot-tab-${this.uid()}`,a.id||=`ot-panel-${this.uid()}`,n.setAttribute(\"aria-controls\",a.id),a.setAttribute(\"aria-labelledby\",n.id))}),t.addEventListener(\"click\",this),t.addEventListener(\"keydown\",this),this.#i&&window.addEventListener(\"hashchange\",this);let e=this.#t.findIndex(n=>n.ariaSelected===\"true\"),s=this.#a();this.#n(Math.max(0,s>=0?s:e),!1)}cleanup(){window.removeEventListener(\"hashchange\",this)}onclick(t){let e=this.#t.indexOf(t.target.closest('[role=\"tab\"]'));e>=0&&this.#n(e)}onkeydown(t){if(!t.target.closest('[role=\"tab\"]'))return;let e=this.keyNav(t,this.activeIndex,this.#t.length,\"ArrowLeft\",\"ArrowRight\");e>=0&&(this.#n(e),this.#t[e].focus())}onhashchange(){let t=this.#a();t>=0&&this.#n(t,!1)}#n(t,e=!0){this.#t.forEach((s,n)=>{let o=n===t;s.ariaSelected=String(o),s.tabIndex=o?0:-1}),this.#e.forEach((s,n)=>s.hidden=n!==t),e&&this.#o(t),this.emit(\"ot-tab-change\",{index:t,tab:this.#t[t]})}#a(){let t=this.#i&&new URLSearchParams(location.hash.slice(1)).get(this.#i);return t?this.#s.indexOf(t):-1}#o(t){if(!this.#i)return;let e=new URLSearchParams(location.hash.slice(1));e.set(this.#i,this.#s[t]);for(let[n,o]of[...e])o||e.delete(n);let s=e.toString();history.replaceState(null,\"\",s?`#${s}`:location.pathname+location.search)}get activeIndex(){return this.#t.findIndex(t=>t.ariaSelected===\"true\")}set activeIndex(t){t>=0&&t<this.#t.length&&this.#n(t)}};customElements.define(\"ot-tabs\",c);var u=class extends d{#t;#e;#i;#s;init(){this.#t=this.querySelector(\"[popover]\"),this.#e=this.querySelector(\"[popovertarget]\"),!(!this.#t||!this.#e)&&(this.#t.addEventListener(\"toggle\",this),this.#t.addEventListener(\"keydown\",this),this.#i=()=>{let t=this.#e.getBoundingClientRect(),e=this.#t.getBoundingClientRect();this.#t.style.top=`${t.bottom+e.height>window.innerHeight?t.top-e.height:t.bottom}px`,this.#t.style.left=`${t.left+e.width>window.innerWidth?t.right-e.width:t.left}px`})}ontoggle(t){t.newState===\"open\"?(this.#i(),window.addEventListener(\"scroll\",this.#i,!0),window.addEventListener(\"resize\",this.#i),this.#s=[...this.querySelectorAll('[role=\"menuitem\"]')],this.#s[0]?.focus(),this.#e.ariaExpanded=\"true\"):(this.cleanup(),this.#s=null,this.#e.ariaExpanded=\"false\",this.#e.focus())}onkeydown(t){if(!t.target.matches('[role=\"menuitem\"]'))return;let e=this.#s.indexOf(t.target),s=this.keyNav(t,e,this.#s.length,\"ArrowUp\",\"ArrowDown\",!0);s>=0&&this.#s[s].focus()}cleanup(){window.removeEventListener(\"scroll\",this.#i,!0),window.removeEventListener(\"resize\",this.#i)}};customElements.define(\"ot-dropdown\",u);var p=class extends d{#t;#e;#i;init(){if(this.#t=this.querySelector('input[type=\"file\"]'),!!this.#t){this.#e=this.querySelector(\"[data-files]\"),this.#i=this.#e?[...this.#e.childNodes]:[],this.classList.add(\"card\",\"vstack\",\"align-center\"),this.#e?.classList.add(\"hstack\",\"justify-center\");for(let t of[\"click\",\"dragover\",\"dragleave\",\"drop\",\"change\"])this.addEventListener(t,this)}}onclick(t){if(this.#t.disabled)return;let e=t.target.closest(\"button\");if(e&&this.#e?.contains(e))return this.#s([...this.#t.files].filter((s,n)=>n!==[...this.#e.children].indexOf(e.parentElement)));t.target!==this.#t&&this.#t.click()}ondragover(t){this.#t.disabled||(t.preventDefault(),t.dataTransfer.dropEffect=\"copy\",this.toggleAttribute(\"data-drag\",!0))}ondragleave(t){this.contains(t.relatedTarget)||this.removeAttribute(\"data-drag\")}ondrop(t){this.#t.disabled||(t.preventDefault(),this.removeAttribute(\"data-drag\"),this.#s([...t.dataTransfer.files].slice(0,this.#t.multiple?1/0:1)))}onchange(){if(!this.#e)return;let t=[...this.#t.files].map(e=>{let s=document.createElement(\"span\");s.className=\"badge\",s.dataset.variant=\"secondary\",s.textContent=e.name;let n=document.createElement(\"button\");return n.type=\"button\",n.ariaLabel=`Remove ${e.name}`,n.textContent=\"\\xD7\",s.append(n),s});this.#e.replaceChildren(...t.length?t:this.#i)}#s(t){let e=new DataTransfer;t.forEach(s=>e.items.add(s)),this.#t.files=e.files,this.#t.dispatchEvent(new Event(\"change\",{bubbles:!0}))}};customElements.define(\"ot-upload\",p);if(\"showPopover\"in HTMLElement.prototype){let i=document.createElement(\"span\");i.className=\"ot-tooltip\",i.popover=\"manual\",i.ariaHidden=\"true\";let t,e=()=>{t&&i.hidePopover(),t=null},s=n=>{let a=(n.type.endsWith(\"out\")?n.relatedTarget:n.target)?.closest?.(\"[title], [data-tooltip]\");if(!a&&t?.matches(\":hover, :focus-visible\")||a===t||(e(),!a))return;let r=a.getAttribute(\"title\");if(r&&(a.dataset.tooltip=r,a.hasAttribute(\"aria-label\")||a.setAttribute(\"aria-label\",r),a.removeAttribute(\"title\")),!a.dataset.tooltip)return;t=a;let l=a.getBoundingClientRect();for(let g of[\"left\",\"top\",\"width\",\"height\"])i.style[g]=`${l[g]}px`;i.dataset.tooltip=a.dataset.tooltip,i.dataset.tooltipPlacement=a.dataset.tooltipPlacement||\"top\",a.after(i),i.showPopover()};for(let n of[\"mouseover\",\"mouseout\",\"focusin\",\"focusout\"])document.addEventListener(n,s);document.addEventListener(\"keydown\",n=>{n.key===\"Escape\"&&e()}),window.addEventListener(\"scroll\",e,!0),window.addEventListener(\"resize\",e)}document.addEventListener(\"click\",i=>{let t=i.target.closest(\"[data-sidebar-toggle]\");if(t){t.closest(\"[data-sidebar-layout]\")?.toggleAttribute(\"data-sidebar-open\");return}if(!i.target.closest(\"[data-sidebar]\")){let e=document.querySelector(\"[data-sidebar-layout][data-sidebar-open]\");e&&window.matchMedia(\"(max-width: 768px)\").matches&&e.removeAttribute(\"data-sidebar-open\")}});var v=i=>document.createElement(i),b=i=>String(i).trim(),f=class extends d{static observedAttributes=[\"disabled\"];#t=new WeakMap;init(){if(this.input=this.querySelector(\"input\"),!this.input)return;this.input.readOnly||(this.input.addEventListener(\"keydown\",this),this.input.addEventListener(\"input\",e=>{e.stopPropagation();let s=this.input.value,n=this.input.list,o=n&&(e.inputType===\"insertReplacementText\"||s.length-(this.prev||\"\").length>1);if(this.prev=s,o){let a=[...n.options].find(r=>r.value===s);if(a)return requestAnimationFrame(()=>this.add(a.data??s))}}),this.input.addEventListener(\"change\",e=>e.stopPropagation()),this.input.addEventListener(\"focus\",this),this.addEventListener(\"click\",this));let t=this.getAttribute(\"value\");t&&(this.value=[t]),this.attributeChangedCallback()}attributeChangedCallback(){this.input&&(this.input.disabled=this.disabled),this.setAttribute(\"aria-disabled\",this.disabled)}get disabled(){return this.hasAttribute(\"disabled\")}set disabled(t){this.toggleAttribute(\"disabled\",!!t)}onkeydown(t){if(t.key===\"Backspace\"&&!this.input.value)return this.remove(this.input.previousElementSibling);(t.key===\"Enter\"||t.key===\",\")&&(t.preventDefault(),this.add(this.input.value))}onfocus(){this.input.list&&(this.input.dispatchEvent(new Event(\"input\",{bubbles:!0})),requestAnimationFrame(()=>{try{this.input.showPicker()}catch{}}))}onclick(t){if(this.disabled)return;let e=t.target.closest(\"button\");e?this.remove(e.parentElement):this.input.focus()}add(t,e){let s=b(t);if(!s||this.value.some(o=>b(o)===s))return;let n=v(\"span\");if(n.className=\"badge\",n.dataset.variant=\"secondary\",n.textContent=s,t&&typeof t==\"object\"&&this.#t.set(n,t),this.input.readOnly)this.insertBefore(n,this.input);else{let o=v(\"button\");o.type=\"button\",o.ariaLabel=`Remove ${s}`,o.textContent=\"\\xD7\",n.appendChild(o),this.insertBefore(n,this.input),this.input.value=this.prev=\"\",this.input.list?.replaceChildren()}e||this.emit(\"input\",this.value)}remove(t){t&&(t.remove(),this.emit(\"input\",this.value))}get value(){return[...this.querySelectorAll(\".badge\")].map(t=>this.#t.get(t)??t.firstChild.data)}set value(t){this.input??=this.querySelector(\"input\"),this.querySelectorAll(\".badge\").forEach(e=>e.remove()),(Array.isArray(t)?t:[]).forEach(e=>{typeof e==\"string\"?e.split(\",\").forEach(s=>this.add(s,!0)):this.add(e,!0)})}};customElements.define(\"ot-taginput\",f);var h={};function A(i){if(!h[i]){let t=document.createElement(\"div\");t.className=\"toast-container\",t.setAttribute(\"popover\",\"manual\"),t.setAttribute(\"data-placement\",i),document.body.appendChild(t),h[i]=t}return h[i]}function E(i,t={}){let{placement:e=\"top-right\",duration:s=4e3}=t,n=A(e);i.classList.add(\"toast\");let o,a=()=>{s>0&&(o=setTimeout(()=>L(i,n),s))};return i.onmouseenter=()=>clearTimeout(o),i.onmouseleave=a,i.setAttribute(\"data-entering\",\"\"),n.appendChild(i),n.showPopover(),requestAnimationFrame(()=>{requestAnimationFrame(()=>{i.removeAttribute(\"data-entering\")})}),a(),i}function L(i,t){if(i.hasAttribute(\"data-exiting\"))return;i.setAttribute(\"data-exiting\",\"\");let e=()=>{i.remove(),t.children.length||t.hidePopover()};i.addEventListener(\"transitionend\",e,{once:!0});let s=getComputedStyle(i).getPropertyValue(\"--transition\").trim(),n=parseFloat(s),o=s.endsWith(\"ms\")?n:n*1e3;setTimeout(e,o)}function y(i,t,e={}){let{variant:s=\"info\",...n}=e,o=document.createElement(\"output\");if(o.setAttribute(\"data-variant\",s),t){let r=document.createElement(\"h6\");r.className=\"toast-title\",r.textContent=t,o.appendChild(r)}let a=document.createElement(\"div\");return a.className=\"toast-message\",a.textContent=i,o.appendChild(a),E(o,n)}function w(i,t={}){let e;if(i instanceof HTMLTemplateElement?e=i.content.firstElementChild?.cloneNode(!0):i&&(e=i.cloneNode(!0)),!!e)return e.removeAttribute(\"id\"),E(e,t)}function x(i){(i?[h[i]]:Object.values(h)).forEach(t=>{t&&(t.innerHTML=\"\",t.hidePopover())})}var m=window.ot||(window.ot={});m.toast=y;m.toast.el=w;m.toast.clear=x;})();\n",
    "import type { RawHtml } from '../ui/html.ts';\nimport { define, CeElement } from '../ui/base.ts';\nimport { CeView, PAGE_TITLE_EVENT } from '../ui/view.ts';\nimport { html } from '../ui/html.ts';\nimport { currentPath, linkPath, matchRoute, navigate, onRouteChange } from './router.ts';\nimport type { RouteDef, RouteMatch } from './router.ts';\nimport { ROUTES } from './routes.ts';\nimport { tabTitle } from './tab-title.ts';\nimport { toastError } from '../ui/toast.ts';\nimport { UNAUTHORIZED_EVENT } from '../http/errors.ts';\nimport { t } from '../ui/i18n.ts';\nimport { confirmLeave, hasUnsavedChanges } from '../ui/leave-guard.ts';\nimport type { CeBreadcrumbsComponent } from './ce-breadcrumbs.component.ts';\nimport './ce-breadcrumbs.component.ts';\nimport './ce-header.component.ts';\n\nconst SLOW_VIEW_MS = 300;\n\n// Views live in light DOM (slotted): password managers do not search shadow roots.\nclass CeAppComponent extends CeElement {\n  #unsubscribe: (() => void) | null = null;\n  #renderToken = 0;\n  #shown: { route: RouteDef | null; view: Element } | null = null;\n  #shownUrl = '';\n\n  readonly #onUnauthorized = (): void => {\n    if (currentPath() !== '/login') {\n      navigate(`/login?next=${encodeURIComponent(location.pathname + location.search)}`);\n    }\n  };\n\n  constructor() {\n    super();\n    this.addEventListener('click', (event) => {\n      // Shadow roots retarget event.target to the host; composedPath() still holds the anchor.\n      const anchor = event.composedPath().find((target): target is HTMLAnchorElement => target instanceof HTMLAnchorElement);\n      if (!anchor) {\n        return;\n      }\n      const path = linkPath(event, { href: anchor.href, target: anchor.target, download: anchor.hasAttribute('download') }, location.origin);\n      if (path === null) {\n        return;\n      }\n      event.preventDefault();\n      void this.#follow(path);\n    });\n    this.addEventListener(PAGE_TITLE_EVENT, (event) => {\n      if (event.target === this.#shown?.view) {\n        this.#showPage();\n      }\n    });\n  }\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.#unsubscribe = onRouteChange(() => {\n      void this.#onRouteChange();\n    });\n    window.addEventListener(UNAUTHORIZED_EVENT, this.#onUnauthorized);\n  }\n\n  override disconnectedCallback(): void {\n    super.disconnectedCallback();\n    this.#unsubscribe?.();\n    window.removeEventListener(UNAUTHORIZED_EVENT, this.#onUnauthorized);\n  }\n\n  override afterRender(): void {\n    this.#renderView();\n  }\n\n  async #follow(path: string): Promise<void> {\n    if (await confirmLeave()) {\n      navigate(path);\n    }\n  }\n\n  // Back and Forward have moved the URL already, so declining puts the shown page's URL back.\n  async #onRouteChange(): Promise<void> {\n    const url = location.pathname + location.search;\n    if (this.#shownUrl !== '' && url !== this.#shownUrl && hasUnsavedChanges() && !(await confirmLeave())) {\n      history.pushState(null, '', this.#shownUrl);\n      return;\n    }\n    this.#renderView();\n  }\n\n  // Re-rendering would rebuild the view; the header, the breadcrumbs and the views re-render themselves.\n  override languageChanged(): void {\n    this.#showPage();\n  }\n\n  async #viewElement(path: string): Promise<{ match: RouteMatch | null; view: Element }> {\n    const match = matchRoute(ROUTES, path);\n    if (match) {\n      return { match, view: await match.route.view(match.params) };\n    }\n\n    const view = document.createElement('p');\n    view.className = 'empty';\n    view.dataset.testid = 'not-found';\n    view.dataset.path = path;\n    view.textContent = t().notFound(path);\n    return { match, view };\n  }\n\n  #renderView(): void {\n    // navigate() dispatches popstate for the current path too, so this runs re-entrantly.\n    const token = ++this.#renderToken;\n\n    void this.#swapView(currentPath(), token);\n  }\n\n  async #swapView(path: string, token: number): Promise<void> {\n    let match: RouteMatch | null;\n    let view: Element;\n    try {\n      ({ match, view } = await this.#viewElement(path));\n    } catch (cause) {\n      toastError(cause);\n      return;\n    }\n\n    if (token !== this.#renderToken || !this.isConnected) {\n      return;\n    }\n\n    // A CeView loads once connected, so it is connected hidden and revealed when ready.\n    if (view instanceof CeView) {\n      view.hidden = true;\n      this.append(view);\n      const slow = new Promise<void>((resolve) => {\n        setTimeout(resolve, SLOW_VIEW_MS);\n      });\n      await Promise.race([view.ready, slow]);\n      if (token !== this.#renderToken || !view.isConnected) {\n        view.remove();\n        return;\n      }\n    }\n\n    // Not replaceChildren: moving a connected view would reconnect it, and it would load again.\n    for (const child of Array.from(this.children)) {\n      if (child !== view) {\n        child.remove();\n      }\n    }\n    if (view.parentNode !== this) {\n      this.append(view);\n    }\n    view.removeAttribute('hidden');\n    this.#shown = { route: match?.route ?? null, view };\n    this.#shownUrl = location.pathname + location.search;\n    this.#showPage();\n    window.scrollTo({ top: 0, behavior: 'instant' });\n  }\n\n  #showPage(): void {\n    if (!this.#shown) {\n      return;\n    }\n    const { route, view } = this.#shown;\n    const viewTitle = view instanceof CeView ? view.pageTitle : null;\n    const name = route ? (viewTitle ?? route.title?.(t()) ?? null) : t().error[404].title;\n    document.title = tabTitle(name);\n    if (view instanceof HTMLElement && view.dataset.testid === 'not-found') {\n      view.textContent = t().notFound(view.dataset.path ?? '');\n    }\n    const breadcrumbs = this.$<CeBreadcrumbsComponent>('ce-breadcrumbs');\n    if (breadcrumbs) {\n      const parents =\n        (view instanceof CeView ? view.pageParents : null) ?? (route?.parents ?? []).map((parent) => ({ path: parent.path, label: parent.label(t()) }));\n      breadcrumbs.trail = parents.length > 0 ? { parents, current: name ?? '' } : null;\n    }\n  }\n\n  override template(): RawHtml {\n    return html`\n      <ce-header data-testid=\"header\"></ce-header>\n\n      <main class=\"container\"><ce-breadcrumbs data-testid=\"breadcrumbs\"></ce-breadcrumbs><slot data-testid=\"view-slot\"></slot></main>\n    `;\n  }\n}\n\nawait define('ce-app', CeAppComponent, \"/app/ce-app.component.ts\");\n",
    "import type { RouteDef } from '../../app/router.ts';\n\nexport const authRoutes: RouteDef[] = [\n  {\n    pattern: /^\\/login\\/?$/,\n    keys: [],\n    view: async () => {\n      const { CeLoginComponent } = await import('./ce-login.component.ts');\n      return new CeLoginComponent();\n    },\n    title: (l) => l.login.title,\n  },\n];\n",
    "import type { RouteDef } from '../../app/router.ts';\n\nexport const recipesRoutes: RouteDef[] = [\n  {\n    pattern: /^\\/$/,\n    keys: [],\n    view: async () => {\n      const { CeRecipeListComponent } = await import('./ce-recipe-list.component.ts');\n      return new CeRecipeListComponent();\n    },\n    title: (l) => l.recipes,\n    nav: { path: '/', label: (l) => l.recipes },\n  },\n  {\n    pattern: /^\\/recipes\\/new\\/?$/,\n    keys: [],\n    view: async () => {\n      const { CeRecipeEditComponent } = await import('./ce-recipe-edit.component.ts');\n      return new CeRecipeEditComponent();\n    },\n    title: (l) => l.recipe.newRecipe,\n    parents: [{ path: '/', label: (l) => l.recipes }],\n  },\n  {\n    pattern: /^\\/recipes\\/import\\/?$/,\n    keys: [],\n    view: async () => {\n      const { CeRecipeImportComponent } = await import('./ce-recipe-import.component.ts');\n      return new CeRecipeImportComponent();\n    },\n    title: (l) => l.recipe.import.title,\n    parents: [{ path: '/', label: (l) => l.recipes }],\n  },\n  {\n    pattern: /^\\/recipes\\/(\\d+)\\/edit\\/?$/,\n    keys: ['id'],\n    view: async ({ id }) => {\n      const { CeRecipeEditComponent } = await import('./ce-recipe-edit.component.ts');\n      const view = new CeRecipeEditComponent();\n      view.setAttribute('recipe-id', id ?? '');\n      return view;\n    },\n    title: (l) => l.edit,\n    // The recipe's own crumb comes from the view, which knows its title.\n    parents: [{ path: '/', label: (l) => l.recipes }],\n  },\n  {\n    pattern: /^\\/recipes\\/(\\d+)\\/?$/,\n    keys: ['id'],\n    view: async ({ id }) => {\n      const { CeRecipeDetailComponent } = await import('./ce-recipe-detail.component.ts');\n      const view = new CeRecipeDetailComponent();\n      view.setAttribute('recipe-id', id ?? '');\n      return view;\n    },\n    title: (l) => l.recipe.recipe,\n    parents: [{ path: '/', label: (l) => l.recipes }],\n  },\n];\n",
    "import { authRoutes } from '../features/auth/auth.routes.ts';\nimport { recipesRoutes } from '../features/recipes/recipes.routes.ts';\nimport type { RouteDef } from './router.ts';\n\n// Spread order is the header order.\nexport const ROUTES: readonly RouteDef[] = [...recipesRoutes, ...authRoutes];\n",
    "// Must match the <title> in index.html.\nexport const APP_TITLE = 'Cena';\n\nexport function tabTitle(name: string | null): string {\n  return name === null ? APP_TITLE : `${name} · Cena`;\n}\n",
    "import type { RawHtml } from '../ui/html.ts';\nimport { define, CeElement } from '../ui/base.ts';\nimport { html } from '../ui/html.ts';\nexport interface Trail {\n  parents: readonly { path: string; label: string }[];\n  current: string;\n}\n\nexport class CeBreadcrumbsComponent extends CeElement {\n  #trail: Trail | null = null;\n\n  get trail(): Trail | null {\n    return this.#trail;\n  }\n\n  set trail(trail: Trail | null) {\n    this.#trail = trail;\n    this.hidden = trail === null;\n    this.render();\n  }\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.hidden = this.#trail === null;\n  }\n\n  override template(): RawHtml {\n    if (!this.#trail) {\n      return html``;\n    }\n    return html`\n      <nav aria-label=\"Breadcrumb\" data-testid=\"breadcrumb\">\n        <ol class=\"unstyled\">\n          ${this.#trail.parents.map((parent) => html`<li><a class=\"unstyled\" href=\"${parent.path}\" data-testid=\"crumb\">${parent.label}</a></li>`)}\n          <li><span aria-current=\"page\" data-testid=\"current\">${this.#trail.current}</span></li>\n        </ol>\n      </nav>\n    `;\n  }\n}\n\nawait define('ce-breadcrumbs', CeBreadcrumbsComponent, \"/app/ce-breadcrumbs.component.ts\");\n",
    "import type { RawHtml } from '../ui/html.ts';\nimport { define, CeElement } from '../ui/base.ts';\nimport { html } from '../ui/html.ts';\nimport { currentPath, isActive, navigate, onRouteChange } from './router.ts';\nimport { ROUTES } from './routes.ts';\nimport { toastError } from '../ui/toast.ts';\nimport { authFacade } from '../features/auth/auth.facade.ts';\nimport { t } from '../ui/i18n.ts';\nimport './ce-language-switch.component.ts';\nimport './ce-theme-toggle.component.ts';\n\nclass CeHeaderComponent extends CeElement {\n  #unsubscribe: (() => void) | null = null;\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.#unsubscribe = onRouteChange(() => {\n      this.#syncLinks();\n    });\n  }\n\n  override disconnectedCallback(): void {\n    super.disconnectedCallback();\n    this.#unsubscribe?.();\n    this.#unsubscribe = null;\n  }\n\n  override afterRender(): void {\n    this.#syncLinks();\n    this.$('nav')?.classList.toggle('auth', authFacade.enabled());\n    // Oat focuses the first item on open; this listener is registered after Oat's, so it wins.\n    this.$<HTMLElement>('menu[popover]')?.addEventListener('toggle', (event) => {\n      if (event.newState === 'open') {\n        this.$<HTMLAnchorElement>('menu a[aria-current=\"page\"]')?.focus();\n      }\n    });\n  }\n\n  // Canceled here, before ce-app routes the link, so the session ends before the login page opens.\n  override async handleAction(action: string, _element: HTMLElement, event: Event): Promise<void> {\n    if (action !== 'logout') {\n      return;\n    }\n    event.preventDefault();\n    try {\n      await authFacade.logout();\n    } catch (error) {\n      toastError(error);\n      return;\n    }\n    navigate('/login');\n  }\n\n  #syncLinks(): void {\n    this.$('nav')?.classList.toggle('login', /^\\/login\\/?$/.test(currentPath()));\n    const links = this.$$<HTMLAnchorElement>('nav a[data-path]');\n    for (const link of links) {\n      if (isActive(link.dataset.path ?? '')) {\n        link.setAttribute('aria-current', 'page');\n      } else {\n        link.removeAttribute('aria-current');\n      }\n    }\n    const menu = this.$<HTMLElement>('menu[popover]');\n    if (menu?.matches(':popover-open') === true) {\n      menu.hidePopover();\n    }\n  }\n\n  override template(): RawHtml {\n    const navItems = ROUTES.flatMap((route) => (route.nav ? [route.nav] : []));\n    return html`\n      <header>\n        <nav class=\"container\" data-testid=\"nav\">\n          <a class=\"brand\" href=\"/\">\n            <img src=\"/icons/icon.svg\" alt=\"\" />\n            <strong>Cena</strong>\n          </a>\n          <ul class=\"links unstyled\" data-testid=\"links\">\n            ${navItems.map((item) => html`<li><a href=\"${item.path}\" data-path=\"${item.path}\">${item.label(t())}</a></li>`)}\n            <li class=\"logout\"><a href=\"/login\" data-action=\"logout\" data-testid=\"links-logout\">${t().logOut}</a></li>\n          </ul>\n          <ot-dropdown class=\"menu\">\n            <button type=\"button\" class=\"ghost icon\" popovertarget=\"nav-menu\" aria-label=\"${t().menu}\">\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                viewBox=\"0 0 24 24\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n                stroke-width=\"2\"\n                stroke-linecap=\"round\"\n                aria-hidden=\"true\"\n              >\n                <path d=\"M4 6h16M4 12h16M4 18h16\" />\n              </svg>\n            </button>\n            <menu popover id=\"nav-menu\" data-testid=\"menu\">\n              ${navItems.map((item) => html`<a role=\"menuitem\" href=\"${item.path}\" data-path=\"${item.path}\">${item.label(t())}</a>`)}\n              <a role=\"menuitem\" class=\"logout\" href=\"/login\" data-action=\"logout\" data-testid=\"menu-logout\">${t().logOut}</a>\n            </menu>\n          </ot-dropdown>\n          <ce-language-switch></ce-language-switch>\n          <ce-theme-toggle></ce-theme-toggle>\n        </nav>\n      </header>\n    `;\n  }\n}\n\nawait define('ce-header', CeHeaderComponent, \"/app/ce-header.component.ts\");\n",
    "import type { RawHtml } from '../ui/html.ts';\nimport { define, CeElement } from '../ui/base.ts';\nimport { html } from '../ui/html.ts';\nimport { LANGUAGE_IDS, LANGUAGES, language, setLanguage } from '../ui/i18n.ts';\nimport type { LanguageId } from '../i18n/language.ts';\n\nclass CeLanguageSwitchComponent extends CeElement {\n  override handleAction(action: string, element: HTMLElement): void {\n    const id = element.dataset.language;\n    // The re-render the change triggers replaces the open menu with a closed one.\n    if (action === 'language' && (id === 'en' || id === 'de')) {\n      setLanguage(id satisfies LanguageId);\n    }\n  }\n\n  override template(): RawHtml {\n    const current = language();\n    return html`\n      <ot-dropdown>\n        <button type=\"button\" class=\"ghost\" popovertarget=\"language-menu\" aria-label=\"${current.language}: ${current.meta.name}\" data-testid=\"toggle\">\n          ${current.meta.name}\n        </button>\n        <menu popover id=\"language-menu\" data-testid=\"menu\">\n          ${LANGUAGE_IDS.map(\n            (id) =>\n              html`<button\n                type=\"button\"\n                role=\"menuitem\"\n                class=\"ghost\"\n                lang=\"${id}\"\n                ${id === current.meta.id ? html`aria-current=\"true\"` : ''}\n                data-action=\"language\"\n                data-language=\"${id}\"\n                data-testid=\"language-${id}\"\n              >\n                ${LANGUAGES[id].meta.name}\n              </button>`,\n          )}\n        </menu>\n      </ot-dropdown>\n    `;\n  }\n}\n\nawait define('ce-language-switch', CeLanguageSwitchComponent, \"/app/ce-language-switch.component.ts\");\n",
    "import type { RawHtml } from '../ui/html.ts';\nimport { define, CeElement } from '../ui/base.ts';\nimport { html } from '../ui/html.ts';\nimport { t } from '../ui/i18n.ts';\nimport { currentTheme, onThemeChange, toggleTheme } from '../ui/theme.ts';\n\nclass CeThemeToggleComponent extends CeElement {\n  #stopThemeSync: (() => void) | null = null;\n\n  override connectedCallback(): void {\n    super.connectedCallback();\n    this.#stopThemeSync = onThemeChange(() => {\n      this.#syncIcon();\n    });\n  }\n\n  override disconnectedCallback(): void {\n    super.disconnectedCallback();\n    this.#stopThemeSync?.();\n    this.#stopThemeSync = null;\n  }\n\n  override afterRender(): void {\n    this.#syncIcon();\n  }\n\n  // In place rather than re-rendering, so the button keeps focus across its own click.\n  #syncIcon(): void {\n    const dark = currentTheme() === 'dark';\n    this.$('svg.sun')?.toggleAttribute('hidden', dark);\n    this.$('svg.moon')?.toggleAttribute('hidden', !dark);\n    this.$('button')?.setAttribute('aria-label', dark ? t().theme.turnOffDark : t().theme.turnOnDark);\n  }\n\n  override handleAction(action: string): void {\n    if (action === 'toggle-theme') {\n      toggleTheme();\n    }\n  }\n\n  override template(): RawHtml {\n    return html`\n      <button type=\"button\" class=\"ghost icon\" data-action=\"toggle-theme\" data-testid=\"toggle\">\n        <svg\n          class=\"sun\"\n          data-testid=\"sun\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          stroke-width=\"2\"\n          stroke-linecap=\"round\"\n          aria-hidden=\"true\"\n        >\n          <circle cx=\"12\" cy=\"12\" r=\"4\" />\n          <path d=\"M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4\" />\n        </svg>\n        <svg\n          class=\"moon\"\n          data-testid=\"moon\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          stroke-width=\"2\"\n          stroke-linecap=\"round\"\n          stroke-linejoin=\"round\"\n          aria-hidden=\"true\"\n        >\n          <path d=\"M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z\" />\n        </svg>\n      </button>\n    `;\n  }\n}\n\nawait define('ce-theme-toggle', CeThemeToggleComponent, \"/app/ce-theme-toggle.component.ts\");\n",
    "import { THEME_STORAGE_KEY } from './storage-keys.ts';\n\nexport type Theme = 'light' | 'dark';\n\nconst EVENT = 'ce-theme-change';\n\nexport const THEMES: readonly Theme[] = ['light', 'dark'];\n\n// `--card` from ui/app.css; also in index.html's no-flash script.\nexport const THEME_COLORS: Readonly<Record<Theme, string>> = { light: '#fff', dark: '#202024' };\n\nfunction isTheme(value: string | null): value is Theme {\n  return value === 'light' || value === 'dark';\n}\n\nfunction systemTheme(): Theme {\n  try {\n    return matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n  } catch {\n    return 'light';\n  }\n}\n\nfunction readStoredTheme(): Theme {\n  try {\n    const stored = localStorage.getItem(THEME_STORAGE_KEY);\n    if (isTheme(stored)) {\n      return stored;\n    }\n  } catch {}\n  return systemTheme();\n}\n\nlet current = readStoredTheme();\n\nexport function currentTheme(): Theme {\n  return current;\n}\n\nfunction applyTheme(): void {\n  document.documentElement.setAttribute('data-theme', current);\n  document.querySelector('meta[name=\"theme-color\"]')?.setAttribute('content', THEME_COLORS[current]);\n}\n\nexport function setTheme(theme: Theme): void {\n  if (theme === current) {\n    return;\n  }\n  current = theme;\n\n  try {\n    localStorage.setItem(THEME_STORAGE_KEY, theme);\n  } catch {}\n\n  applyTheme();\n  window.dispatchEvent(new CustomEvent(EVENT, { detail: { theme } }));\n}\n\nexport function toggleTheme(): void {\n  setTheme(current === 'dark' ? 'light' : 'dark');\n}\n\nexport function onThemeChange(listener: () => void): () => void {\n  window.addEventListener(EVENT, listener);\n  return () => {\n    window.removeEventListener(EVENT, listener);\n  };\n}\n\n// index.html's script leaves the attribute off when unstored, so Oat paints the first frame.\napplyTheme();\n"
  ],
  "mappings": "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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAA+pI,8CAA8C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAiY,6CAA6C;AAAA;AAAA;AAAA,EAAkC,+BAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAy4E,4BAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAoK,oCAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAgoB,qCAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAA0T,wCAAwC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAmQ,mDAAmD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAA6nC,mDAAmD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAqP,iDAAiD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAA4c,iDAAiD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAikC,qDAAqD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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",
  "debugId": "16B69FD4C20D305064756E2164756E21",
  "names": []
}