/* ----------------------------------------------------------------------------
   Rent — application design system
   ----------------------------------------------------------------------------
   Layered on top of Oat.ink (oat.css). Oat provides tokens, base elements, and
   a handful of components. This file establishes the typographic identity and
   the app-specific composition primitives (page-header, kv, metric, toolbar,
   filter-bar, etc.). See README §Design system.
   ---------------------------------------------------------------------------- */

/* --- Typography ---------------------------------------------------------- */

/* Bump the root font-size 20% above the user-agent default (typically
   16px → 19.2px). Oat sizes everything in rem, so all type, spacing tokens
   that use rem, and components scale with this. */
html { font-size: 120%; }

:root {
  /* Editorial / land-registry pairing.
     - Fraunces: variable display serif (opt-size 9..144, wght 400..700).
       Used for headings, eyebrows, brand, and the metric figure.
     - Public Sans: USWDS workhorse, refined and readable at body sizes.
     - JetBrains Mono: numerals on the ledger.
     The fonts are loaded via Google Fonts in the layout. */
  --font-display: "Fraunces", ui-serif, Georgia, "Times New Roman", serif;
  --font-sans: "Public Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, Consolas, monospace;

  /* Tighten the heading rhythm — Oat's defaults are generous; with a serif
     display face we want a touch less air above each H1. */
  --shell-pad-block: var(--space-6);
  --shell-pad-inline: var(--space-6);
}

body, dialog, [popover] { font-family: var(--font-sans); }
h1, h2, h3, h4, h5, h6 { font-family: var(--font-display); font-feature-settings: "ss01"; letter-spacing: -0.01em; }
h1 { font-weight: 500; letter-spacing: -0.02em; }

/* Money and IDs read as data, not prose. */
code, .mono { font-family: var(--font-mono); }

/* Badges are metadata, never display type. They live next to titles, inside
   tables, anywhere — always render in the body face with normal tracking. */
.badge { font-family: var(--font-sans); letter-spacing: 0; font-weight: 500; }

/* --- Shell: topbar, sidebar, main padding ------------------------------- */

/* Topbar lives inside the sidebar grid (row 1, full width) so the aside's
   100dvh sibling never pushes the logout button below the viewport. */
[data-sidebar-layout] > .topbar { grid-column: 1 / -1; grid-row: 1; }
:root { --topbar-height: calc(2rem + var(--space-3) * 2); }
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--topbar-height);
  padding: 0 var(--shell-pad-inline);
  border-bottom: 1px solid var(--border);
}
.topbar h1 { margin: 0; }
.topbar .topbar-right { display: flex; gap: var(--space-3); align-items: center; font-size: var(--text-7); color: var(--muted-foreground); }

/* The ☰ glyph isn't in Public Sans — force a font that has it. The toggle
   is a navigation chrome control, not a CTA, so override the framework's
   primary-button styling to be transparent + foreground-coloured. */
[data-sidebar-toggle] {
  font-family: system-ui, sans-serif;
  font-size: 1.25rem;
  line-height: 1;
  background: transparent;
  color: var(--foreground);
  border: 1px solid var(--border);
}
[data-sidebar-toggle]:hover { background: var(--faint); }

/* The brand wordmark lives in the topbar. Sets the tone of the whole app. */
.brand {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.35rem;
  letter-spacing: -0.015em;
  font-variation-settings: "opsz" 96;
  line-height: 1;
}
.brand a { color: inherit; text-decoration: none; display: inline-flex; align-items: center; }
.brand a:hover { color: var(--primary); }
.brand-logo { display: inline-flex; align-items: center; line-height: 0; }
.brand-logo > svg, .brand-logo > img { display: block; height: 2rem; width: auto; max-width: 100%; }

/* Three-column layout for the theme color fields on the settings page. */
.theme-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
}
@media (max-width: 720px) {
  .theme-grid { grid-template-columns: 1fr; }
}

.theme-swatches {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
  max-width: 32rem;
  margin-block: var(--space-3);
}
.theme-swatch {
  display: flex;
  align-items: stretch;
  height: 4.5rem;
  padding: 0;
  border: 2px solid var(--border);
  border-radius: var(--radius-medium);
  cursor: pointer;
  overflow: hidden;
  background: transparent;
}
.theme-swatch[aria-pressed="true"] {
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary);
}
.theme-swatch-bg {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 500;
}
.theme-swatch-accent {
  width: 1rem;
}
.settings-logo-preview {
  display: inline-block;
  margin-inline-end: var(--space-3);
  vertical-align: middle;
}
.settings-logo-preview img {
  display: block;
  max-height: 3rem;
  width: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-medium);
  background: var(--faint);
  padding: var(--space-1);
}

/* Pad the main content frame; the sidebar nav has its own internal padding. */
[data-sidebar-layout] > main > div { padding: var(--shell-pad-block) var(--shell-pad-inline); }

/* Sidebar */
[data-sidebar] > nav { padding: var(--space-5) var(--space-3); }
[data-sidebar] > nav a { font-size: var(--text-7); font-weight: 500; padding-block: var(--space-2); }
.nav-group {
  display: block;
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint-foreground);
  padding: var(--space-2) var(--space-3) var(--space-1);
  margin-block-start: var(--space-3);
}
.nav-group:first-child { margin-block-start: 0; }
.nav-group::before {
  content: "";
  display: inline-block;
  width: 1.25rem;
  height: 1px;
  background: currentColor;
  margin-inline-end: 0.6em;
  vertical-align: 0.25em;
  opacity: 0.7;
}

/* --- Flash ---------------------------------------------------------------- */

.flash { padding: var(--space-3) var(--space-4); margin-bottom: var(--space-4); border-radius: var(--radius-medium); border-inline-start: 3px solid currentColor; font-size: var(--text-7); }
.flash-notice { background: color-mix(in srgb, var(--success) 8%, transparent); color: var(--success); }
.flash-alert  { background: color-mix(in srgb, var(--danger) 10%, transparent);  color: var(--danger); }

/* --- Eyebrow (the signature micro-detail) ------------------------------- */

/* Used everywhere a small uppercase label is needed: page-header, metric,
   sidebar groups, section heads. The leading hairline rule is the visual
   fingerprint that ties the whole shell together. */
.eyebrow {
  display: block;
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.eyebrow::before {
  content: "";
  display: inline-block;
  width: 1.5rem;
  height: 1px;
  background: currentColor;
  margin-inline-end: 0.6em;
  vertical-align: 0.25em;
  opacity: 0.7;
}

/* --- Page primitives ----------------------------------------------------- */

/* Title row: eyebrow + h1 (+ optional badges) on the left, action toolbar
   on the right. Falls back gracefully when either slot is empty. */
.page-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: 0 0 var(--space-8);
  padding-block-end: var(--space-4);
  border-bottom: 1px solid var(--border);
}
.page-header > :first-child { min-width: 0; }
.page-header h1 {
  margin: var(--space-2) 0 0;
  font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.25rem);
  font-variation-settings: "opsz" 144;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.page-header .eyebrow { margin-block-end: 0; }

/* Section head: h2 paired with inline actions. */
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: var(--space-10) 0 var(--space-4);
  padding-block-end: var(--space-2);
  border-bottom: 1px solid var(--border);
}
.section-head h2 { margin: 0; font-size: var(--text-3); font-weight: 500; font-variation-settings: "opsz" 48; }

/* Breadcrumbs */
nav[aria-label="Breadcrumb"] { margin-block-end: var(--space-4); font-size: var(--text-7); color: var(--muted-foreground); }
nav[aria-label="Breadcrumb"] ol { gap: var(--space-2); }
nav[aria-label="Breadcrumb"] a { color: inherit; text-decoration: none; transition: color var(--transition-fast); }
nav[aria-label="Breadcrumb"] a:hover { color: var(--foreground); }
nav[aria-label="Breadcrumb"] [aria-hidden="true"] { opacity: 0.4; }
nav[aria-label="Breadcrumb"] strong { color: var(--foreground); font-weight: 500; }

/* --- Toolbar (action rows) ----------------------------------------------- */

/* Collapses the trailing-newline gap that button_to forms introduce so a row
   of <%= button_to %> calls renders inline. Use anywhere two or more actions
   sit together. The .small modifier is for in-table-cell action clusters. */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
.toolbar > form { margin: 0; display: inline-flex; }
.toolbar > a { white-space: nowrap; }
/* oat.css forces inputs to width:100% (catches submit/reset). Restore auto so
   actions sit inline alongside other toolbar items. */
.toolbar input[type="submit"], .toolbar input[type="reset"], .toolbar input[type="button"] { width: auto; }
.toolbar.small { gap: var(--space-1); flex-wrap: nowrap; }

/* --- Definition-list grid for label/value summaries --------------------- */

.kv {
  display: grid;
  grid-template-columns: minmax(7rem, max-content) 1fr;
  gap: var(--space-2) var(--space-5);
  margin: 0;
}
.kv dt { color: var(--muted-foreground); font-size: var(--text-7); font-weight: 500; }
.kv dd { margin: 0; font-size: var(--text-7); }
@media (max-width: 540px) {
  .kv { grid-template-columns: 1fr; gap: var(--space-1) 0; }
  .kv dt { margin-block-start: var(--space-2); }
}

/* --- Metric (hero number block) ----------------------------------------- */

.metric { display: flex; flex-direction: column; gap: var(--space-2); }
.metric .figure {
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 1.7rem + 2vw, 3.25rem);
  font-weight: 400;
  line-height: 0.95;
  letter-spacing: -0.025em;
  font-variation-settings: "opsz" 144;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--foreground);
}
.metric .caption { font-size: var(--text-8); color: var(--muted-foreground); }

/* --- Dashboard KPI tiles ------------------------------------------------ */

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-3);
  margin: 0 0 var(--space-6);
}
@media (max-width: 900px) { .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 480px) { .kpi-grid { grid-template-columns: 1fr; } }

.kpi-tile {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-5);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-medium);
  position: relative;
}
.kpi-tile .figure {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem);
  font-weight: 400; line-height: 1; letter-spacing: -0.02em;
  font-variation-settings: "opsz" 144;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--foreground);
}
.kpi-tile .figure-suffix {
  font-size: 0.55em; color: var(--muted-foreground); margin-inline-start: 0.15em;
  letter-spacing: 0;
}
.kpi-tile .caption { font-size: var(--text-8); color: var(--muted-foreground); }
.kpi-tile-alert { border-color: color-mix(in srgb, var(--danger, #b91c1c) 50%, var(--border)); }
.kpi-tile-alert .figure { color: var(--danger, #b91c1c); }

.dashboard-card { padding: 0; overflow: hidden; }
.dashboard-card + .dashboard-card { margin-top: var(--space-6); }
.dashboard-card-header {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.dashboard-card-header h2 { margin: 0; font-size: var(--text-6); }
.dashboard-card .table { margin: 0; }
.dashboard-card > .empty { padding: var(--space-5); margin: 0; }
.ghost-link { font-size: var(--text-8); color: var(--muted-foreground); text-decoration: none; }
.ghost-link:hover { color: var(--foreground); }

.income-chart { margin: 0; padding: var(--space-4) var(--space-5); }
.income-chart svg { display: block; width: 100%; height: auto; }
.income-chart-bar { fill: var(--accent, #2563eb); }
.income-chart-bar:hover { fill: color-mix(in srgb, var(--accent, #2563eb) 75%, white); }
.income-chart-grid { stroke: var(--border); stroke-width: 1; }
.income-chart-axis { fill: var(--muted-foreground); font-size: 11px; font-family: inherit; }
.income-chart-year { font-weight: 600; }

/* --- Cards --------------------------------------------------------------- */

/* Override Oat's drop-shadow card. We want printed-page surfaces: a hairline
   border, a subtle inset highlight on top, no float. */
.card {
  background-color: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-medium);
  box-shadow: none;
  padding: var(--space-6);
  position: relative;
}
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 1px 0 0 rgb(from var(--foreground) r g b / 0.04);
}

/* Summary card: card + responsive split between metric and kv. */
.summary-card {
  display: grid;
  grid-template-columns: minmax(12rem, 1fr) 2fr;
  gap: var(--space-8);
  align-items: start;
}
@media (max-width: 640px) {
  .summary-card { grid-template-columns: 1fr; gap: var(--space-5); }
}

/* Lease summary partial: tinted with the theme's primary color so the
   "current lease" callout reads as the operational focus of the page. */
.lease-summary {
  background: color-mix(in srgb, var(--primary) 8%, var(--card));
  border-color: color-mix(in srgb, var(--primary) 35%, var(--border));
}

/* Stimulus-driven dropdown menu. Use on <div class="menu" data-controller="menu">
   with a <button data-action="menu#toggle"> trigger and a
   <div class="menu-items" data-menu-target="items"> panel. The controller
   toggles the [open] attribute on the wrapper; the panel pops out absolutely
   positioned so it doesn't reflow neighbours. */
.menu { position: relative; align-self: flex-start; }
.menu > button::after {
  content: "▾";
  font-size: 0.75em;
  margin-inline-start: var(--space-1);
  opacity: 0.7;
}
.menu[open] > button { background: var(--faint); }
.menu .menu-items {
  position: absolute;
  inset-inline-end: 0;
  inset-block-start: calc(100% + var(--space-1));
  z-index: 10;
  display: none;
  flex-direction: column;
  min-width: 9rem;
  padding: var(--space-1);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-medium);
  box-shadow: 0 6px 24px rgb(from #000 r g b / 0.18);
}
@media (max-width: 720px) {
  .menu .menu-items { inset-inline-end: auto; inset-inline-start: 0; }
}
.menu[open] .menu-items { display: flex; }
.menu .menu-items form { margin: 0; display: block; }
.menu .menu-items button,
.menu .menu-items > a {
  width: 100%;
  justify-content: flex-start;
  border: 0;
  background: transparent;
  color: var(--foreground);
  border-radius: var(--radius-small);
  font-size: var(--text-7);
  font-weight: var(--font-medium);
  line-height: var(--leading-normal);
  padding: var(--space-2) var(--space-3);
  text-decoration: none;
}
.menu .menu-items button:hover,
.menu .menu-items > a:hover {
  background: var(--faint);
}
.menu .menu-items button[data-variant="danger"] { color: var(--danger); }

/* --- Filter bar ---------------------------------------------------------- */

/* The standard index-page filter strip. Hosts:
   - .scope (REQUIRED): the canonical "tabs" for switching between record sets.
     Always render *every* scope as either a link or a <strong> for the active
     one — never an ad-hoc "Show / Hide archived" toggle. The active scope
     reads as a bold underlined tab; the rest are muted links. This is how
     all index pages must expose Active / Archived / Inactive / etc.
   - inline search inputs (optional). The form uses display:contents so its
     fields land inline next to .scope.
   The .toggle class is reserved for things that genuinely toggle (e.g.
   collapsing details), not for switching scopes. */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  margin-block-end: var(--space-5);
  padding-block-end: var(--space-3);
  border-bottom: 1px dashed var(--border);
  font-size: var(--text-7);
}
.filter-bar .scope { display: inline-flex; gap: var(--space-3); }
.filter-bar .scope + .scope { padding-inline-start: var(--space-4); border-inline-start: 1px solid var(--border); }
.filter-bar .scope a, .filter-bar .scope strong {
  color: var(--muted-foreground);
  text-decoration: none;
  font-weight: 500;
  padding-block-end: var(--space-1);
  border-block-end: 2px solid transparent;
}
.filter-bar .scope a:hover { color: var(--foreground); }
.filter-bar .scope strong { color: var(--foreground); border-block-end-color: var(--primary); }
.filter-bar .toggle { color: var(--muted-foreground); text-decoration: none; }
.filter-bar .toggle:hover { color: var(--foreground); }
.filter-bar form { display: contents; }

/* Inline filter-form layout: stack [data-field] groups on mobile, lay them
   out in a single row on desktop. Used by the audit-log search. */
.filter-form-row { display: grid; grid-template-columns: 1fr; gap: var(--space-3); align-items: end; }
.filter-form-row [data-field] { margin-block-end: 0; }
.filter-form-row .toolbar { justify-self: start; }
@media (min-width: 769px) {
  .filter-form-row { grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)) auto; gap: var(--space-4); }
}

/* --- Tables -------------------------------------------------------------- */

/* overscroll-behavior keeps the table's horizontal scroll from hijacking
   page-scroll wheel events; width:100% on the inner <table> keeps a
   sub-pixel mismatch from triggering a scrollbar when the content fits. */
.table { overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; border: 1px solid var(--border); border-radius: var(--radius-medium); }
.table table { width: 100%; font-size: var(--text-7); }
.table thead { background: var(--faint); }
.table th { font-weight: 500; color: var(--muted-foreground); font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; padding: var(--space-3) var(--space-4); }
.table td { padding: var(--space-3) var(--space-4); }
.table tbody tr:hover { background: color-mix(in srgb, var(--muted) 40%, transparent); }
.table td.num, .table th.num { text-align: end; font-variant-numeric: tabular-nums lining-nums; font-family: var(--font-mono); font-size: 0.8rem; }
.table .actions { width: 1%; white-space: nowrap; }

/* Tabular numerals for money columns in any data table (works without .table wrap too). */
td.num, th.num { text-align: end; font-variant-numeric: tabular-nums lining-nums; }

/* --- Empty state --------------------------------------------------------- */

.empty {
  padding: var(--space-10) var(--space-6);
  text-align: center;
  color: var(--muted-foreground);
  border: 1px dashed var(--border);
  border-radius: var(--radius-medium);
  font-size: var(--text-7);
}

/* --- Inline details (audit) --------------------------------------------- */

details.inline {
  border: none;
  border-radius: 0;
  padding: 0;
  background: transparent;
  display: inline;
  overflow: visible;
}
details.inline > summary { display: inline; cursor: pointer; opacity: 0.6; }
details.inline > summary::after { display: none; }
details.inline > dl { margin-top: 0.4rem; padding-left: 0.75rem; border-left: 2px solid var(--border); }
details.inline > dl dt { font-weight: 500; opacity: 0.6; }
details.inline > dl dd { margin: 0 0 0.25rem 0; }

/* --- Public shell -------------------------------------------------------- */

.public-shell {
  max-width: 56rem;
  margin: 4rem auto;
  padding: 2rem;
}

.public-brand { display: flex; justify-content: center; margin-bottom: 2.5rem; }
.public-brand .brand-logo > svg, .public-brand .brand-logo > img { height: 8rem; }

.receipt { max-width: 32rem; }

.public-hero { text-align: center; margin: 0 0 2.5rem; }
.public-hero h1 { margin: 0 auto 1.5rem; max-width: 28rem; }
.public-hero-subtitle { margin: 0 0 1.25rem; color: var(--text-muted, #71717a); font-size: 1.1rem; }
.public-hero-stats { margin: 0 0 1.75rem; color: var(--text-muted, #71717a); font-size: 1rem; letter-spacing: 0.02em; }
.public-hero-back { margin: 0 0 1rem; font-size: 0.95rem; }
.public-hero form { display: inline-block; margin: 0; }

@media (max-width: 640px) {
  .public-shell { margin: 1.5rem auto; padding: 1.25rem; }
  .public-brand { margin-bottom: 1.5rem; }
  .public-brand .brand-logo > svg, .public-brand .brand-logo > img { height: 6.5rem; }
  .public-hero h1 { font-size: clamp(1.75rem, 8vw, 2.5rem); margin-bottom: 1.25rem; }
  .public-hero form { display: block; width: 100%; }
  .public-hero form button[type="submit"] { width: 100%; }
}

/* --- Turbo navigation progress -------------------------------------------- */

/* Turbo sets aria-busy="true" on <html> during navigation, and on the
   <form>/<a>/<button> being submitted. Oat's global
   [aria-busy=true]::before injects an inline-block spinner — but as an
   inline element it pushes neighbouring content sideways or down,
   shifting layout the moment the user clicks. Suppress the inline
   spinner everywhere; the Turbo progress bar below the topbar is the
   single navigation affordance we keep. */
[aria-busy="true"]::before { content: none !important; display: none !important; }

/* Turbo injects .turbo-progress-bar at the body level and sets it to
   position:fixed via inline JS. Pinning the styling here so it can never
   participate in layout flow (which is what causes the "bump" some users
   report when the bar is briefly inserted before its inline styles apply). */
.turbo-progress-bar {
  position: fixed !important;
  top: calc(var(--topbar-height) - 1px);
  left: 0;
  height: 2px;
  width: 0;
  background: var(--primary);
  z-index: 9999;
  pointer-events: none;
}

/* --- Property thumbnail (used on receipts, tenant cards, etc.) ---------- */

/* The standard small property image used on operational records (transaction
   receipts, tenant summary cards). Always rendered via `property_thumb` in
   PropertiesHelper so the wrapping link and alt text are consistent.
   Square-cropped to read as an avatar/seal next to a kv summary. Scoped to
   <img> so the larger card-sized .property-thumb wrapper isn't shrunk. */
img.property-thumb {
  display: block;
  width: 4.5rem;
  height: 4.5rem;
  object-fit: cover;
  border-radius: var(--radius-medium);
  border: 1px solid var(--border);
}

/* --- Photo carousel + thumbs (property edit) ---------------------------- */

/* Two-column admin layout for property show: gallery left, summary +
   description right. Falls back to a single column on narrow viewports. */
.property-show-grid {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(20rem, 2fr);
  gap: var(--space-8);
  align-items: start;
}
.property-show-details {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.property-show-grid .photo-carousel { margin: 0; }
@media (max-width: 900px) {
  .property-show-grid { grid-template-columns: 1fr; gap: var(--space-5); }
}

.photo-carousel { position: relative; margin: var(--space-4) 0; }
.photo-carousel-track {
  display: flex; gap: var(--space-2); overflow-x: auto;
  scroll-snap-type: x mandatory; scrollbar-width: none; -ms-overflow-style: none;
}
.photo-carousel-track::-webkit-scrollbar { display: none; }
.photo-carousel-slide { flex: 0 0 100%; scroll-snap-align: center; margin: 0; }
.photo-carousel-slide img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--radius-medium); display: block;
}
.photo-carousel-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  background: rgba(0,0,0,0.4); color: #fff; border: none;
  width: 2.25rem; height: 2.25rem; border-radius: 999px;
  font-size: 1.5rem; line-height: 1; cursor: pointer;
}
.photo-carousel-nav.prev { left: 0.5rem; }
.photo-carousel-nav.next { right: 0.5rem; }
@media (max-width: 768px) { .photo-carousel-nav { display: none; } }

.photo-thumbs { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.photo-thumbs li { display: flex; flex-direction: column; gap: var(--space-1); align-items: center; }
.photo-thumbs li img {
  display: block; width: 8rem; height: 8rem; object-fit: cover;
  border-radius: var(--radius-small); border: 1px solid var(--border);
}
.photo-thumb-actions { display: flex; gap: 0.25rem; }
.photo-thumb-actions form.inline { display: inline; }
.photo-thumb-actions button { padding: 0 0.5rem; min-width: 2rem; }

/* --- Trix --------------------------------------------------------------- */

trix-editor {
  background: var(--background);
  color: var(--foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-small);
  min-height: 8rem;
  padding: var(--space-2);
}
trix-toolbar { margin-bottom: var(--space-1); }
trix-toolbar .trix-button--icon::before { filter: var(--trix-icon-filter, none); }
trix-toolbar .trix-button-group--file-tools { display: none; }

/* --- Print -------------------------------------------------------------- */

@media print {
  .topbar, [data-sidebar], .no-print { display: none !important; }
  [data-sidebar-layout] { display: block; }
  main, .receipt { padding: 0; max-width: 100%; }
  .page-header { border: none; }
}
