'use client';

/**
 * Domain - section 9 of /design-system.
 *
 * Renders every domain component with all relevant variants.
 * The grand tour: any prop combination that matters in the FDS shows up here.
 */

import { useEffect, useState } from 'react';
import { GroupProgressBar } from '@/components/ui/domain/GroupProgressBar';
import { ActivityRow } from '@/components/ui/domain/ActivityRow';
import { AddressCard } from '@/components/ui/domain/AddressCard';
import { BusinessHeader } from '@/components/ui/domain/BusinessHeader';
import { BusinessProfile } from '@/components/ui/domain/BusinessProfile';
import { CountdownTimer } from '@/components/ui/domain/CountdownTimer';
import { DealCard, DraftDealCard } from '@/components/ui/domain/DealCard';
import { AmbientDealCard } from '@/features/deals/AmbientDealCard';
import { DealRow } from '@/components/ui/domain/DealRow';
import { DealTypePicker } from '@/components/ui/domain/DealTypePicker';
import type { DealType } from '@/components/ui/domain/DealCard';
import { GalleryCarousel } from '@/components/ui/domain/GalleryCarousel';
import { HeroCarousel } from '@/components/ui/domain/HeroCarousel';
import { HoursEditor, type DayHours, type DayOfWeek } from '@/components/ui/domain/HoursEditor';
import { DraftsBanner } from '@/components/ui/domain/DraftsBanner';
import { type DraftSummary } from '@/components/ui/domain/DraftsList';
import { ClubBadge } from '@/components/ui/domain/ClubBadge';
import { ClubHero } from '@/components/ui/domain/ClubHero';
import { MapThumbnail } from '@/components/ui/domain/MapThumbnail';
import { MetricTile } from '@/components/ui/domain/MetricTile';
import {
  NotificationsToggleList,
  type NotificationItem,
} from '@/components/ui/domain/NotificationsToggleList';
import { PaymentMethodChip } from '@/components/ui/domain/PaymentMethodChip';
import { PostPurchaseReaction } from '@/components/ui/domain/PostPurchaseReaction';
import { PriceDisplay } from '@/components/ui/domain/PriceDisplay';
import { QrButton, QrCodeCard } from '@/components/ui/domain/QrCodeCard';
import { VoucherCardFan } from '@/components/ui/domain/VoucherCardFan';
import { RejectionBanner } from '@/components/ui/domain/RejectionBanner';
import { ReportButton } from '@/components/ui/domain/ReportButton';
import { RejectionBannerList } from '@/components/ui/domain/RejectionBannerList';
import { ReviewCard } from '@/components/ui/domain/ReviewCard';
import { VendorLogoFallback } from '@/components/ui/domain/VendorLogoFallback';
import { ShareButton } from '@/components/ui/domain/ShareButton';
import { StatusPill, type DealState } from '@/components/ui/domain/StatusPill';
import { SummaryBar } from '@/components/ui/domain/SummaryBar';
import { TechnicalReviewCard } from '@/components/ui/domain/TechnicalReviewCard';
import { VendorCard, type VendorCardData } from '@/components/ui/domain/VendorCard';
import { VendorModeBadge } from '@/components/ui/domain/VendorModeBadge';
import { AdminLinkBadge } from '@/components/ui/domain/AdminLinkBadge';
import { CategoryPillGroup } from '@/components/ui/domain/CategoryPillGroup';
import { TagPillGroup } from '@/components/ui/domain/TagPillGroup';
import { VoucherCheckbox } from '@/components/ui/domain/VoucherCheckbox';
import { IsraeliAddressField } from '@/components/ui/domain/IsraeliAddressField';
import { DealCTA } from '@/components/ui/domain/DealCTA';
import { VendorHeroPreview } from '@/components/ui/domain/VendorHeroPreview';
import { DealDurationPicker } from '@/components/ui/domain/DealDurationPicker';
import { PickupAddressSelector } from '@/components/ui/domain/PickupAddressSelector';
import { PickupHoursField } from '@/components/ui/domain/PickupHoursField';
import { TagPills } from '@/components/ui/domain/TagPills';
import { AnimatedNumber } from '@/components/ui/domain/AnimatedNumber';
import { Sparkline } from '@/components/ui/domain/Sparkline';
// ─── Cart domain components (add-to-cart W1-B + W2-B) ─────────────────────────
import { CartIconButton } from '@/components/ui/domain/cart/CartIconButton';
import { CartDrawer } from '@/components/ui/domain/cart/CartDrawer';
import { CartLineItem, type CartLineItemData } from '@/components/ui/domain/cart/CartLineItem';
import { CartEmptyState } from '@/components/ui/domain/cart/CartEmptyState';
import { CartStaleToast, type RemovedItem } from '@/components/ui/domain/cart/CartStaleToast';
import { CartNavButton } from '@/components/ui/domain/cart/CartNavButton';
import { QuantityStepper } from '@/components/ui/primitives/QuantityStepper';
import { AvatarUploadButton } from '@/components/ui/domain/AvatarUploadButton';
import { BirthdayField } from '@/components/ui/domain/BirthdayField';
import { CitySelectField, type CityValue } from '@/components/ui/domain/CitySelectField';
// ─── Near You components (Wave 3) ─────────────────────────────────────────────
import { CitySelector } from '@/components/ui/domain/CitySelector';
import { UseMyLocationButton } from '@/components/ui/domain/UseMyLocationButton';
import { RadiusSlider } from '@/components/ui/domain/RadiusSlider';
import {
  OpeningHoursFilter,
  type OpeningHoursValue,
} from '@/components/ui/domain/OpeningHoursFilter';
import { ViewToggle, type ViewMode } from '@/components/ui/domain/ViewToggle';
import { CategoryFacet } from '@/components/ui/domain/facets/CategoryFacet';
import { TagFacet } from '@/components/ui/domain/facets/TagFacet';
import { DealTypeFacet } from '@/components/ui/domain/facets/DealTypeFacet';
import type { DealType as FacetDealType } from '@/components/ui/domain/facets/DealTypeFacet';
import { DiscountTierFacet } from '@/components/ui/domain/facets/DiscountTierFacet';
import type { DiscountTier } from '@/components/ui/domain/facets/DiscountTierFacet';
import { PriceRangeFacet } from '@/components/ui/domain/facets/PriceRangeFacet';
import { MapView } from '@/components/ui/domain/MapView';
import { StatusBadge, type StatusEntry } from '@/components/ui/domain/StatusBadge';
import { OnboardingSheet } from '@/features/onboarding/OnboardingSheet';
import { Button } from '@/components/ui/primitives/Button';
import { Stack } from '@/components/ui/layout/Stack';
import { Row } from '@/components/ui/layout/Row';
import { Grid } from '@/components/ui/layout/Grid';
import { CategoriesTabsNav } from '@/components/ui/layout/CategoriesTabsNav';
import { useT } from '@/lib/i18n/react';
import { ComponentEntry } from '../../components/ComponentEntry';
import { SectionShell } from '../../components/SectionShell';
// ─── Vendor dashboard domain components ───────────────────────────────────────
import { VendorKpiTile } from '@/components/ui/domain/vendor/VendorKpiTile';
import { VendorActivityFeed } from '@/components/ui/domain/vendor/VendorActivityFeed';
import { VendorDealCard } from '@/components/ui/domain/vendor/VendorDealCard';
import { VendorFunnelChart } from '@/components/ui/domain/vendor/VendorFunnelChart';
import { PhonePreview } from '@/components/ui/domain/vendor/PhonePreview';
import { TierEditor } from '@/components/ui/domain/vendor/TierEditor';
import { AiRecommendationCard } from '@/components/ui/domain/vendor/AiRecommendationCard';
import { HoursRow } from '@/components/ui/domain/vendor/HoursRow';
import { VendorSidebar } from '@/components/ui/layout/VendorSidebar';
import { VendorBottomNav } from '@/components/ui/layout/VendorBottomNav';
// ─── Admin domain components (admin-makeover/foundation) ──────────────────────
import { LanguageRegistryTable } from '@/components/ui/admin/LanguageRegistryTable';
import { AdminTable } from '@/components/ui/domain/admin/AdminTable';
import { AdminListPage } from '@/components/ui/domain/admin/AdminListPage';
import { AdminDetailPage } from '@/components/ui/domain/admin/AdminDetailPage';
import { AdminFilterBar } from '@/components/ui/domain/admin/AdminFilterBar';
import { Pagination } from '@/components/ui/primitives/Pagination';
import { AdminBackLink } from '@/components/ui/domain/admin/AdminBackLink';
import { AiVerdictPanel } from '@/components/ui/domain/admin/AiVerdictPanel';
import { CascadePreviewPanel } from '@/components/ui/domain/admin/CascadePreviewPanel';
// ─── /deals/all filter + browser components ───────────────────────────────────
import { DealsBrowserIsland } from '@/components/ui/domain/DealsBrowser/DealsBrowserIsland';
import { SortDropdown } from '@/components/ui/domain/SortDropdown/SortDropdown';
import type { BrowseSort } from '@/lib/enums/search-sort';
import { FilterChip } from '@/components/ui/primitives/FilterChip';
import { formatAgorotWhole, formatShekelFloat } from '@/lib/money';
import {
  SAMPLE_DEAL,
  SAMPLE_DEAL_GROUP,
  SAMPLE_VENDOR_DEAL,
  SAMPLE_DEAL_ROW_ACTIVE,
  SAMPLE_DEAL_ROW_PENDING,
  SAMPLE_DEAL_ROW_REJECTED,
  SAMPLE_DEAL_ROW_EXPIRED,
  SAMPLE_DEAL_ROW_SOLD_OUT,
} from '../../sample-data';

const SAMPLE_PNG =
  'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"><rect width="200" height="200" fill="white"/><rect x="20" y="20" width="40" height="40" fill="black"/><rect x="140" y="20" width="40" height="40" fill="black"/><rect x="20" y="140" width="40" height="40" fill="black"/><rect x="80" y="80" width="40" height="40" fill="black"/></svg>';

// StatusBadge demo: state→tone+labelKey map using real `returns` ns keys.
const STATUS_BADGE_DEMO_MAP: Record<string, StatusEntry> = {
  requested: { tone: 'info', labelKey: 'status_requested' },
  approved: { tone: 'warning', labelKey: 'status_approved' },
  refunded: { tone: 'success', labelKey: 'status_refunded' },
  denied: { tone: 'danger', labelKey: 'status_denied' },
};

// Reference timestamp for the design-system demos, resolved once at module
// eval (render stays pure for React Compiler memoization). Must be live wall
// clock — CountdownTimer demos stage on time-to-end, and a fixed past date
// renders every demo as expired.
const DEMO_NOW = Date.now();
const IN_ONE_HOUR = new Date(DEMO_NOW + 60 * 60 * 1000).toISOString();
const IN_ONE_MINUTE = new Date(DEMO_NOW + 60 * 1000).toISOString();
const IN_SIX_HOURS = new Date(DEMO_NOW + 6 * 60 * 60 * 1000).toISOString();
const IN_24_HOURS = new Date(DEMO_NOW + 24 * 60 * 60 * 1000).toISOString();
const SAMPLE_VOUCHERS = [
  { voucherId: 'voucher-1', qrPngUrl: SAMPLE_PNG, expiresAt: IN_24_HOURS },
  { voucherId: 'voucher-2', qrPngUrl: SAMPLE_PNG, expiresAt: IN_24_HOURS },
  { voucherId: 'voucher-3', qrPngUrl: SAMPLE_PNG, expiresAt: IN_24_HOURS },
];
const HALF_HOUR_AGO = new Date(DEMO_NOW - 30 * 60 * 1000).toISOString();
const ONE_HOUR_AGO = new Date(DEMO_NOW - 60 * 60 * 1000).toISOString();
const ONE_DAY_AGO = new Date(DEMO_NOW - 24 * 60 * 60 * 1000).toISOString();
const FOUR_DAYS_AGO = new Date(DEMO_NOW - 4 * 24 * 60 * 60 * 1000).toISOString();
const SEVEN_DAYS_AGO = new Date(DEMO_NOW - 7 * 24 * 60 * 60 * 1000).toISOString();

const ALL_DAYS: DayOfWeek[] = [
  'sunday',
  'monday',
  'tuesday',
  'wednesday',
  'thursday',
  'friday',
  'saturday',
];
const DEFAULT_DAY: DayHours = { open: true, openTime: '09:00', closeTime: '22:00' };
const SAMPLE_HOURS = ALL_DAYS.reduce(
  (acc, d) => ({ ...acc, [d]: DEFAULT_DAY }),
  {} as Record<DayOfWeek, DayHours>,
);

const ALL_STATES: DealState[] = [
  'ACTIVE',
  'PENDING_APPROVAL',
  'REJECTED',
  'EXPIRED',
  'SOLD_OUT',
  'PAUSED',
];

function DealDurationPickerDemo() {
  const [start, setStart] = useState<string | undefined>(undefined);
  const [end, setEnd] = useState<string | undefined>(undefined);
  return (
    <div className="flex flex-col gap-6">
      <div>
        <p className="text-text-muted mb-2 text-xs">Friday open (shows end-of-week preset)</p>
        <DealDurationPicker
          startValue={start}
          endValue={end}
          onStartChange={setStart}
          onEndChange={setEnd}
          businessHours={{
            thursdayClosed: false,
            thursdayClose: '20:00',
            fridayClosed: false,
            fridayClose: '14:00',
          }}
        />
      </div>
      <div>
        <p className="text-text-muted mb-2 text-xs">Friday closed — Thu is last day</p>
        <DealDurationPicker
          startValue={start}
          endValue={end}
          onStartChange={setStart}
          onEndChange={setEnd}
          businessHours={{
            thursdayClosed: false,
            thursdayClose: '20:00',
            fridayClosed: true,
            fridayClose: null,
          }}
        />
      </div>
      <div>
        <p className="text-text-muted mb-2 text-xs">No businessHours — end-of-week hidden</p>
        <DealDurationPicker
          startValue={start}
          endValue={end}
          onStartChange={setStart}
          onEndChange={setEnd}
          businessHours={null}
        />
      </div>
    </div>
  );
}

export function Domain() {
  const t = useT('design_system');
  const [dealType, setDealType] = useState<DealType>('COUPON');
  const [hours, setHours] = useState(SAMPLE_HOURS);
  const [reactionKey, setReactionKey] = useState(0);
  const [notificationToggles, setNotificationToggles] = useState({
    sales: true,
    stock: true,
    personal: false,
    team: true,
  });
  const [silentMode, setSilentMode] = useState(false);
  const [animatedCount, setAnimatedCount] = useState(3);
  const [animatedMoney, setAnimatedMoney] = useState(8900);

  useEffect(() => {
    const timer = window.setInterval(() => {
      setAnimatedCount((value) => (value >= 11 ? 2 : value + 4));
      setAnimatedMoney((value) => (value >= 18900 ? 8900 : value + 5000));
    }, 100);
    return () => window.clearInterval(timer);
  }, []);

  // ─── Cart demo state ───────────────────────────────────────────────────────
  const [cartDrawerOpen, setCartDrawerOpen] = useState(false);
  const [cartQty, setCartQty] = useState(2);
  const [stepperQty, setStepperQty] = useState(1);
  const SAMPLE_CART_ITEMS: CartLineItemData[] = [
    {
      dealId: 'demo-deal-1',
      imageId:
        'data:image/svg+xml,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 width%3D%2264%22 height%3D%2264%22%3E%3Crect fill%3D%22%23e5e7eb%22 width%3D%22100%25%22 height%3D%22100%25%22%2F%3E%3C%2Fsvg%3E',
      title: 'ארוחת בוקר ב-50%',
      vendorName: 'קפה בן גוריון',
      unitPrice: 4500,
      qty: cartQty,
      maxQty: 5,
      qtyTiers: [],
    },
    {
      dealId: 'demo-deal-2',
      imageId:
        'data:image/svg+xml,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 width%3D%2264%22 height%3D%2264%22%3E%3Crect fill%3D%22%23dbeafe%22 width%3D%22100%25%22 height%3D%22100%25%22%2F%3E%3C%2Fsvg%3E',
      title: 'פיצה משפחתית',
      vendorName: 'פיצריה נאפולי',
      unitPrice: 6900,
      qty: 1,
      maxQty: 3,
      qtyTiers: [],
    },
  ];
  const SAMPLE_REMOVED_ITEMS: RemovedItem[] = [
    { dealId: 'r1', title: 'עוגת גבינה', reason: 'expired' },
    { dealId: 'r2', title: 'שייק תות', reason: 'sold_out' },
  ];

  const notifItems: NotificationItem[] = [
    {
      key: 'sales',
      label: 'התראה על כל מכירה',
      enabled: notificationToggles.sales,
      onChange: (v) => setNotificationToggles((p) => ({ ...p, sales: v })),
    },
    {
      key: 'stock',
      label: 'התראה כשהמלאי אוזל',
      enabled: notificationToggles.stock,
      onChange: (v) => setNotificationToggles((p) => ({ ...p, stock: v })),
    },
    {
      key: 'personal',
      label: 'התראה על בקשת דיל אישי',
      enabled: notificationToggles.personal,
      onChange: (v) => setNotificationToggles((p) => ({ ...p, personal: v })),
    },
    {
      key: 'team',
      label: 'הודעה מצוות מולטידיל',
      enabled: notificationToggles.team,
      onChange: (v) => setNotificationToggles((p) => ({ ...p, team: v })),
    },
  ];

  return (
    <SectionShell id="domain" title={t('sidebar_domain')} description={t('section_intro')}>
      {/* DealCard */}
      <ComponentEntry
        name="DealCard"
        id="dealcard-domain"
        path="src/components/ui/domain/DealCard"
        description="Deal feed card. Variants: feed-row / grid / scroll. Works for COUPON / GROUP."
        tokens={['radius-xl', 'color-surface-base', 'shadow-sm']}
        a11yNotes={[
          'Real button via onSelect',
          'Image alt required',
          'Timer is aria-live=off (decorative)',
        ]}
        importSnippet={`import { DealCard } from '@/components/ui/domain/DealCard';`}
      >
        <Stack gap="4">
          <Row gap="3" wrap>
            <AmbientDealCard imageSrc={SAMPLE_DEAL.imageSrc}>
              <DealCard deal={SAMPLE_DEAL} variant="feed-row" demo />
            </AmbientDealCard>
            <AmbientDealCard imageSrc={SAMPLE_DEAL_GROUP.imageSrc}>
              <DealCard deal={SAMPLE_DEAL_GROUP} variant="feed-row" demo />
            </AmbientDealCard>
            <AmbientDealCard imageSrc={SAMPLE_VENDOR_DEAL.imageSrc}>
              <DealCard deal={SAMPLE_VENDOR_DEAL} variant="feed-row" demo />
            </AmbientDealCard>
          </Row>
          <Grid cols={3} gap="3">
            <AmbientDealCard imageSrc={SAMPLE_DEAL.imageSrc}>
              <DealCard deal={SAMPLE_DEAL} variant="grid" demo />
            </AmbientDealCard>
            <AmbientDealCard imageSrc={SAMPLE_DEAL_GROUP.imageSrc}>
              <DealCard deal={SAMPLE_DEAL_GROUP} variant="grid" demo />
            </AmbientDealCard>
            <AmbientDealCard imageSrc={SAMPLE_VENDOR_DEAL.imageSrc}>
              <DealCard deal={SAMPLE_VENDOR_DEAL} variant="grid" demo />
            </AmbientDealCard>
          </Grid>
          <div>
            <p className="text-text-muted mb-2 text-sm">variant=scroll (horizontal strip)</p>
            <Row gap="3" wrap={false} className="overflow-x-auto pb-2">
              <AmbientDealCard imageSrc={SAMPLE_DEAL.imageSrc}>
                <DealCard deal={SAMPLE_DEAL} variant="scroll" demo />
              </AmbientDealCard>
              <AmbientDealCard imageSrc={SAMPLE_DEAL_GROUP.imageSrc}>
                <DealCard deal={SAMPLE_DEAL_GROUP} variant="scroll" demo />
              </AmbientDealCard>
            </Row>
          </div>
        </Stack>
      </ComponentEntry>

      {/* DealCard — draft variant */}
      <ComponentEntry
        name="DealCard (draft)"
        id="dealcard-domain-draft"
        path="src/components/ui/domain/DealCard"
        description="Draft variant shown in vendor draft list. Title fallback, relative timestamp, star toggle, resume + delete actions."
        tokens={[
          'color-surface-raised',
          'color-border-default',
          'radius-2xl',
          'color-warning-500',
          'color-danger-600',
        ]}
        a11yNotes={[
          'Star button has aria-pressed (filled = true)',
          'Resume and Delete buttons have explicit aria-label via i18n',
          'article has aria-label = title (or untitled fallback)',
        ]}
        importSnippet={`import { DraftDealCard } from '@/components/ui/domain/DealCard';`}
      >
        <Stack gap="3">
          <div className="max-w-xs">
            <p className="text-text-muted mb-2 text-sm">Starred, with title</p>
            <DraftDealCard
              id="demo-draft-1"
              title="המבורגר ענק + תוספת"
              updatedAt={new Date('2026-04-24T11:55:00.000Z')}
              isStarred={true}
              onToggleStar={() => null}
              onResume={() => null}
              onDelete={() => null}
            />
          </div>
          <div className="max-w-xs">
            <p className="text-text-muted mb-2 text-sm">Unstarred, untitled fallback</p>
            <DraftDealCard
              id="demo-draft-2"
              title={null}
              updatedAt={new Date('2026-04-24T10:00:00.000Z')}
              isStarred={false}
              onToggleStar={() => null}
              onResume={() => null}
              onDelete={() => null}
            />
          </div>
        </Stack>
      </ComponentEntry>

      {/* DealRow */}
      <ComponentEntry
        name="DealRow"
        id="dealrow-domain"
        path="src/components/ui/domain/DealRow"
        description="Vendor dashboard row - image + title + counters + state pill + 3-dot menu."
        tokens={['color-surface-base', 'color-border-default', 'radius-md']}
        a11yNotes={['Action button has aria-label', 'DropdownMenu for keyboard access']}
        importSnippet={`import { DealRow } from '@/components/ui/domain/DealRow';`}
      >
        <Stack gap="3">
          <DealRow deal={SAMPLE_DEAL_ROW_ACTIVE} onPause={() => null} onDuplicate={() => null} />
          <DealRow deal={SAMPLE_DEAL_ROW_PENDING} />
          <DealRow deal={SAMPLE_DEAL_ROW_REJECTED} />
          <DealRow deal={SAMPLE_DEAL_ROW_EXPIRED} />
          <DealRow deal={SAMPLE_DEAL_ROW_SOLD_OUT} />
        </Stack>
      </ComponentEntry>

      {/* MetricTile */}
      <ComponentEntry
        name="MetricTile"
        id="metrictile-domain"
        path="src/components/ui/domain/MetricTile"
        description="Big-number dashboard tile with optional delta, count-up, period label, and sparkline."
        tokens={['color-surface-raised', 'color-success-50', 'color-warning-50', 'radius-xl']}
        a11yNotes={[
          'role=figure with aria-label',
          'AnimatedNumber respects reduced motion',
          'sparkline includes its own aria-label',
        ]}
        importSnippet={`import { MetricTile } from '@/components/ui/domain/MetricTile';`}
      >
        <Grid cols={3} gap="3">
          <MetricTile
            value="₪847"
            label={t('metric_earned')}
            tone="success"
            countUpValue={847}
            formatCountUpValue={(value) => formatShekelFloat(Math.round(value))}
            delta={{ value: 14, dir: 'up', suffix: '%', periodLabel: t('metric_previous_period') }}
            periodLabel={t('metric_current_period')}
            sparklinePoints={[420, 510, 560, 610, 710, 847]}
            sparklineLabel={t('metric_revenue_trend')}
          />
          <MetricTile value="23" label={t('metric_customers')} />
          <MetricTile value="8" label={t('metric_returned')} tone="success" />
        </Grid>
      </ComponentEntry>

      <ComponentEntry
        name="Sparkline"
        id="sparkline-domain"
        path="src/components/ui/domain/Sparkline"
        description="Tiny trend chart for dashboard cards and health strips."
        tokens={['color-text-muted', 'color-success-600', 'color-warning-600', 'color-danger-600']}
        a11yNotes={[
          'Rendered as an svg with required aria-label',
          'Falls back to a baseline when only one point exists',
        ]}
        importSnippet={`import { Sparkline } from '@/components/ui/domain/Sparkline';`}
      >
        <Grid cols={3} gap="3">
          <Sparkline aria-label="Revenue trend" points={[14, 20, 18, 26, 31, 29]} tone="success" />
          <Sparkline aria-label="Alert trend" points={[4, 7, 6, 8, 5, 3]} tone="warning" />
          <Sparkline aria-label="Flat trend" points={[1]} />
        </Grid>
      </ComponentEntry>

      {/* BusinessHeader */}
      <ComponentEntry
        name="BusinessHeader"
        id="businessheader-domain"
        path="src/components/ui/domain/BusinessHeader"
        description="Blue header block for the BusinessPage with name, hours, and joined-via strip."
        tokens={['color-brand-primary-700', 'color-text-on-brand']}
        a11yNotes={['<header> landmark with h1', 'Club slot accepts ClubBadge']}
        importSnippet={`import { BusinessHeader } from '@/components/ui/domain/BusinessHeader';`}
      >
        <BusinessHeader
          businessName="מסעדת רוטשילד 22"
          hours="פתוח עכשיו · 12:00-23:00"
          joinedViaDeal
          isClubMember
        />
      </ComponentEntry>

      {/* BusinessProfile */}
      <ComponentEntry
        name="BusinessProfile"
        id="businessprofile-domain"
        path="src/components/ui/domain/BusinessProfile"
        description="Compact vendor widget (logo, name, rating, address, today's hours) used in deal-detail start sidebar; reusable on any vendor-referencing page."
        tokens={['color-surface-elevated', 'color-border-subtle', 'shadow-md', 'radius-2xl']}
        a11yNotes={[
          '<aside> with aria-label',
          'Star rating wrapped in aria-label with score + count',
          'ChevronLeft mirrors in RTL',
        ]}
        importSnippet={`import { BusinessProfile } from '@/components/ui/domain/BusinessProfile';`}
      >
        <div className="flex flex-col gap-6 lg:flex-row">
          <div className="lg:w-80">
            <BusinessProfile
              vendor={{
                id: 'demo',
                slug: 'demo',
                businessName: 'מאפיית הלחם של רוטשילד',
                displayName: 'מאפיית הלחם',
                logoUrl: null,
                reviewsScore: 4.7,
                reviewsCount: 128,
                address: 'רוטשילד 22, תל אביב',
                city: 'תל אביב',
                todayHours: {
                  weekday: 'monday',
                  closed: false,
                  open: '07:00',
                  close: '22:00',
                },
              }}
            />
          </div>
          <div className="lg:w-80">
            <BusinessProfile
              variant="inline"
              vendor={{
                id: 'demo2',
                slug: 'demo2',
                businessName: 'קפה שישה',
                displayName: 'קפה שישה',
                logoUrl: null,
                reviewsScore: 0,
                reviewsCount: 0,
                address: null,
                city: 'רמת גן',
                todayHours: null,
              }}
            />
          </div>
        </div>
      </ComponentEntry>

      {/* ActivityRow */}
      <ComponentEntry
        name="ActivityRow"
        id="activityrow-domain"
        path="src/components/ui/domain/ActivityRow"
        description="Notification row with colored dot, title, sub, optional action, and relative time."
        tokens={['color-success-500', 'color-warning-500', 'color-danger-500', 'color-neutral-400']}
        a11yNotes={[
          'Dot is aria-hidden',
          'Time is real <time>',
          'Use aria-live=polite on the surrounding list',
        ]}
        importSnippet={`import { ActivityRow } from '@/components/ui/domain/ActivityRow';`}
      >
        <Stack gap="2">
          <ActivityRow
            tone="success"
            title="מכירה - ארוחת בורגר + שתייה"
            sub="נותרו 6 יחידות"
            timestamp={HALF_HOUR_AGO}
          />
          <ActivityRow
            tone="warning"
            title="בקשת דיל אישי"
            sub="לקוח מבקש הצעה אישית"
            timestamp={ONE_HOUR_AGO}
          />
          <ActivityRow
            tone="danger"
            title="הודעה מצוות מולטידיל"
            sub="פסטה ים תיכונית - נדרש עדכון תמונה"
            timestamp={ONE_DAY_AGO}
          />
        </Stack>
      </ComponentEntry>

      {/* SummaryBar */}
      <ComponentEntry
        name="SummaryBar"
        id="summarybar-domain"
        path="src/components/ui/domain/SummaryBar"
        description="Three-column savings/purchases/businesses bar (FDS §4.8)."
        tokens={['color-brand-primary-50', 'color-brand-primary-100']}
        a11yNotes={['region with aria-label', 'Each metric is a labelled cell']}
        importSnippet={`import { SummaryBar } from '@/components/ui/domain/SummaryBar';`}
      >
        <SummaryBar savings={253} purchases={5} businesses={4} />
      </ComponentEntry>

      {/* DraftsBanner */}
      <ComponentEntry
        name="DraftsBanner"
        id="draftsbanner-domain"
        path="src/components/ui/domain/DraftsBanner"
        description="Amber-tinted banner shown on vendor dashboard when the vendor has deal drafts. Links to /vendor/deals/drafts. Dismiss button stores flag in session-scoped zustand store (not persisted)."
        tokens={['color-warning-50', 'color-warning-400', 'color-warning-600', 'color-warning-800']}
        a11yNotes={[
          '<aside role="note"> for non-critical status',
          'Dismiss button has aria-label from i18n (vendorDrafts.dismiss)',
          'Icon is aria-hidden',
          'Link is keyboard-focusable with visible focus ring',
        ]}
        importSnippet={`import { DraftsBanner } from '@/components/ui/domain/DraftsBanner';`}
      >
        <Stack gap="3">
          <DraftsBanner count={1} />
          <DraftsBanner count={5} />
        </Stack>
      </ComponentEntry>

      {/* PriceDisplay */}
      <ComponentEntry
        name="PriceDisplay"
        id="pricedisplay-domain"
        path="src/components/ui/domain/PriceDisplay"
        description="Original strikethrough + discounted brand-color price. Inline + stacked layouts."
        tokens={['color-brand-primary-700', 'color-text-muted']}
        a11yNotes={[
          'aria-label provides full price + percent',
          'Strikethrough applied via line-through, not just CSS',
        ]}
        importSnippet={`import { PriceDisplay } from '@/components/ui/domain/PriceDisplay';`}
      >
        <Stack gap="3">
          <PriceDisplay original={90} discounted={45} layout="inline" size="md" />
          <PriceDisplay original={320} discounted={180} layout="stacked" size="lg" />
          <PriceDisplay original={120} discounted={60} layout="inline" size="sm" />
        </Stack>
      </ComponentEntry>

      {/* CountdownTimer */}
      <ComponentEntry
        name="CountdownTimer"
        id="countdowntimer-domain"
        path="src/components/ui/domain/CountdownTimer"
        description="Live countdown with neutral / warm / urgent stages."
        tokens={['color-timer-neutral', 'color-timer-warm', 'color-timer-urgent']}
        a11yNotes={[
          '<time> element',
          'aria-live=off (visual only)',
          'Pulse animation only on motion-safe + urgent',
        ]}
        importSnippet={`import { CountdownTimer } from '@/components/ui/domain/CountdownTimer';`}
      >
        <Row gap="6" align="center">
          <CountdownTimer endIso={IN_SIX_HOURS} />
          <CountdownTimer endIso={IN_ONE_HOUR} />
          <CountdownTimer endIso={IN_ONE_MINUTE} />
        </Row>
      </ComponentEntry>

      {/* QrCodeCard + QrButton */}
      <ComponentEntry
        name="QrCodeCard / QrButton"
        id="qrcodecard-domain"
        path="src/components/ui/domain/QrCodeCard"
        description="Large QR card + small inline 'QR' button that opens a dialog."
        tokens={['radius-xl', 'shadow-md']}
        a11yNotes={['Img alt comes from i18n', 'Button opens labelled Dialog']}
        importSnippet={`import { QrCodeCard, QrButton } from '@/components/ui/domain/QrCodeCard';`}
      >
        <Row gap="6" align="center" wrap>
          <QrCodeCard qrPngUrl={SAMPLE_PNG} expiryIso={IN_24_HOURS} />
          <QrButton qrPngUrl={SAMPLE_PNG} />
        </Row>
      </ComponentEntry>

      <ComponentEntry
        name="VoucherCardFan"
        id="vouchercardfan-domain"
        path="src/components/ui/domain/VoucherCardFan"
        description="Stacked voucher fan that expands to a single decodable QR card for multi-unit purchases."
        tokens={['radius-xl', 'shadow-md', 'color-brand-primary-500']}
        a11yNotes={[
          'Each voucher is a keyboard-focusable button',
          'Arrow keys move between vouchers; Enter/Space toggle expansion; Escape collapses',
          'Expanded state is exposed via aria-expanded and labelled controls',
        ]}
        importSnippet={`import { VoucherCardFan } from '@/components/ui/domain/VoucherCardFan';`}
      >
        <VoucherCardFan vouchers={SAMPLE_VOUCHERS} className="w-full max-w-sm" />
      </ComponentEntry>

      {/* RejectionBanner */}
      <ComponentEntry
        name="RejectionBanner"
        id="rejection-banner"
        path="src/components/ui/domain/RejectionBanner"
        description="Tinted alert block for rejection feedback. danger (red) + resubmit callback for deal rejections; warning (amber) + replace-image link for image moderation rejections."
        tokens={[
          'color-danger-50',
          'color-danger-500',
          'color-danger-700',
          'color-warning-50',
          'color-warning-500',
          'color-warning-700',
        ]}
        a11yNotes={[
          '<aside role=alert> aria-label from title',
          'Replace CTA is <a> for no-JS navigation',
          'Focus ring on both button + link variants',
        ]}
        importSnippet={`import { RejectionBanner } from '@/components/ui/domain/RejectionBanner';`}
      >
        <div className="flex flex-col gap-3">
          <RejectionBanner
            reason="Image quality too low"
            detail="The hero image is below 37.5rem on the longest side."
            onResubmit={() => null}
          />
          <RejectionBanner
            severity="warning"
            title="הלוגו נדחה"
            reason="The logo contains a watermark."
            replaceHref="/vendor/settings?tab=logo"
          />
        </div>
      </ComponentEntry>

      {/* PostPurchaseReaction */}
      <ComponentEntry
        name="PostPurchaseReaction"
        id="postpurchasereaction-domain"
        path="src/components/ui/domain/PostPurchaseReaction"
        description="Celebration overlay shown after each purchase. Confetti for the first."
        tokens={['color-success-500', 'color-brand-primary-600']}
        a11yNotes={[
          'Confetti respects prefers-reduced-motion',
          'Auto-dismiss timer + manual dismiss button',
        ]}
        importSnippet={`import { PostPurchaseReaction } from '@/components/ui/domain/PostPurchaseReaction';`}
      >
        <Stack gap="3">
          <Button onClick={() => setReactionKey((k) => k + 1)}>Trigger first-purchase mode</Button>
          <PostPurchaseReaction
            key={reactionKey}
            purchaseCount={1}
            totalSavings={45}
            newBusinesses={1}
          />
        </Stack>
      </ComponentEntry>

      {/* VendorModeBadge */}
      <ComponentEntry
        name="VendorModeBadge"
        id="vendormodebadge-domain"
        path="src/components/ui/domain/VendorModeBadge"
        description="Green pill displayed in the top bar when vendor mode is active."
        tokens={['color-success-50', 'color-success-700']}
        a11yNotes={['Decorative - meaning conveyed by surrounding context']}
        importSnippet={`import { VendorModeBadge } from '@/components/ui/domain/VendorModeBadge';`}
      >
        <VendorModeBadge />
      </ComponentEntry>

      {/* AdminLinkBadge */}
      <ComponentEntry
        name="AdminLinkBadge"
        id="adminlinkbadge-domain"
        path="src/components/ui/domain/AdminLinkBadge"
        description="Warning-toned pill link to /admin. Renders null unless isAdmin. Used in customer top bars to give admins a quick entry point from any page."
        tokens={['color-warning-500', 'color-warning-600', 'color-text-inverse', 'radius-sm']}
        a11yNotes={[
          'Rendered as <a>',
          'aria-label via admin_link_aria i18n key',
          'Focus ring visible',
        ]}
        importSnippet={`import { AdminLinkBadge } from '@/components/ui/domain/AdminLinkBadge';`}
      >
        <Row gap="2" wrap>
          <AdminLinkBadge isAdmin={true} />
        </Row>
      </ComponentEntry>

      {/* StatusPill */}
      <ComponentEntry
        name="StatusPill"
        id="statuspill-domain"
        path="src/components/ui/domain/StatusPill"
        description="Pill that maps deal states to tonal Pill colors."
        tokens={['Pill tones - success / warning / danger / info / neutral']}
        a11yNotes={['Localised label per state', 'Color is decorative - text carries meaning']}
        importSnippet={`import { StatusPill } from '@/components/ui/domain/StatusPill';`}
      >
        <Row gap="2" wrap>
          {ALL_STATES.map((s) => (
            <StatusPill key={s} state={s} />
          ))}
        </Row>
      </ComponentEntry>

      {/* ReviewCard */}
      <ComponentEntry
        name="ReviewCard"
        id="reviewcard-domain"
        path="src/components/ui/domain/ReviewCard"
        description="Review with star rating, body, date, and optional vendor reply."
        tokens={['color-warning-500', 'radius-xl']}
        a11yNotes={['Stars are aria-hidden', 'Rating exposed via aria-label']}
        importSnippet={`import { ReviewCard } from '@/components/ui/domain/ReviewCard';`}
      >
        <ReviewCard
          reviewerName="Avi C."
          rating={4}
          body={t('sample_review')}
          date={FOUR_DAYS_AGO}
          vendorReply="תודה רבה!"
        />
      </ComponentEntry>

      {/* TechnicalReviewCard */}
      <ComponentEntry
        name="TechnicalReviewCard"
        id="technicalreviewcard-domain"
        path="src/components/ui/domain/TechnicalReviewCard"
        description="Review variant without stars - used for technical/bug reports."
        tokens={['radius-xl', 'color-surface-base']}
        a11yNotes={['<article> element', 'No rating displayed']}
        importSnippet={`import { TechnicalReviewCard } from '@/components/ui/domain/TechnicalReviewCard';`}
      >
        <TechnicalReviewCard
          reviewerName="Support"
          body="App crashed on iOS 17 when scanning QR."
          date={SEVEN_DAYS_AGO}
        />
      </ComponentEntry>

      {/* GalleryCarousel */}
      <ComponentEntry
        name="GalleryCarousel"
        id="gallerycarousel-domain"
        path="src/components/ui/domain/GalleryCarousel"
        description="Horizontal snap-scroll image gallery (vendor + user variants)."
        tokens={['radius-md', 'space-3']}
        a11yNotes={['region aria-label per variant', 'Image alt required for each item']}
        importSnippet={`import { GalleryCarousel } from '@/components/ui/domain/GalleryCarousel';`}
      >
        <GalleryCarousel
          variant="vendor"
          images={[
            { id: 'g1', src: SAMPLE_DEAL.imageSrc, alt: 'Storefront' },
            { id: 'g2', src: SAMPLE_DEAL.imageSrc, alt: 'Interior' },
            { id: 'g3', src: SAMPLE_DEAL.imageSrc, alt: 'Menu' },
          ]}
        />
      </ComponentEntry>

      {/* MapThumbnail */}
      <ComponentEntry
        name="MapThumbnail"
        id="mapthumbnail-domain"
        path="src/components/ui/domain/MapThumbnail"
        description="Static placeholder block with a pin icon. Tap opens native maps."
        tokens={['color-brand-primary-100', 'radius-md']}
        a11yNotes={['Real <button> with aria-label', 'Falls back to Google Maps web URL']}
        importSnippet={`import { MapThumbnail } from '@/components/ui/domain/MapThumbnail';`}
      >
        <MapThumbnail lat={32.0853} lng={34.7818} address={t('sample_address')} />
      </ComponentEntry>

      {/* ReportButton */}
      <ComponentEntry
        name="ReportButton"
        id="reportbutton-domain"
        path="src/components/ui/domain/ReportButton"
        description="Flag-icon button that opens a report dialog with reason picker."
        tokens={['color-text-muted', 'color-danger-600']}
        a11yNotes={['Real <button> with aria-label', 'Dialog has labelled title + form fields']}
        importSnippet={`import { ReportButton } from '@/components/ui/domain/ReportButton';`}
      >
        <ReportButton targetId="deal-1" targetType="DEAL" />
      </ComponentEntry>

      {/* ShareButton */}
      <ComponentEntry
        name="ShareButton"
        id="sharebutton-domain"
        path="src/components/ui/domain/ShareButton"
        description="Native share-sheet button with clipboard fallback."
        tokens={['color-text-muted']}
        a11yNotes={['Real <button> with aria-label', 'Toast-like feedback after copy']}
        importSnippet={`import { ShareButton } from '@/components/ui/domain/ShareButton';`}
      >
        <ShareButton url="https://multideal.co.il/deals/sample-deal" title="מולטידיל" />
      </ComponentEntry>

      {/* PaymentMethodChip — Stripe-tokenised card display */}
      <ComponentEntry
        name="PaymentMethodChip"
        id="paymentmethodchip-domain"
        path="src/components/ui/domain/PaymentMethodChip"
        description="Stored card chip with last4 and confirm-to-remove flow. Used in profile payment-methods section. Card details are tokenised client-side via Stripe Elements; no PAN stored server-side."
        tokens={['radius-md', 'color-surface-base']}
        a11yNotes={['AlertDialog confirmation', 'Remove button has aria-label']}
        importSnippet={`import { PaymentMethodChip } from '@/components/ui/domain/PaymentMethodChip';`}
      >
        <PaymentMethodChip brandName="Visa" last4="4242" onRemove={() => null} />
      </ComponentEntry>

      {/* AddressCard */}
      <ComponentEntry
        name="AddressCard"
        id="addresscard-domain"
        path="src/components/ui/domain/AddressCard"
        description="Stored address chip with edit + delete actions."
        tokens={['radius-xl', 'color-surface-base']}
        a11yNotes={['Edit + Delete are real buttons with labels', 'Pin icon is aria-hidden']}
        importSnippet={`import { AddressCard } from '@/components/ui/domain/AddressCard';`}
      >
        <AddressCard
          label="בית"
          address={t('sample_address')}
          onEdit={() => null}
          onDelete={() => null}
        />
      </ComponentEntry>

      {/* DealTypeTabs (Astro-only) */}
      <ComponentEntry
        name="DealTypeTabs"
        id="dealtypetabs-domain"
        path="src/components/ui/domain/DealTypeTabs"
        description="Sticky brand-blue header for the public /deals/all listing page: back link + page <h1> + filter tabs. Astro-only; preview below mirrors its rendered markup."
        tokens={[
          'color-brand-primary-700',
          'color-brand-on-primary',
          'z-sticky',
          'space-4',
          'radius-full',
        ]}
        a11yNotes={[
          'Semantic <header> + nested <nav> with aria-label',
          'Active tab carries aria-current="page"',
          'Back link has aria-label; arrow glyph is aria-hidden',
          'Focus-visible ring on every interactive element',
        ]}
        importSnippet={`import DealTypeTabs from '@/components/ui/domain/DealTypeTabs/DealTypeTabs.astro';\n// Usage: <DealTypeTabs title={...} backHref="/" backLabel={...} tabsLabel={...} tabs={[...]} activeValue="" />`}
      >
        <Stack gap="3">
          <header className="bg-brand-primary-700 text-brand-on-primary sticky top-0 z-[var(--z-sticky)] px-4 py-3">
            <div className="flex items-center gap-3">
              <button
                type="button"
                className="text-brand-on-primary/80 hover:text-brand-on-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
                aria-label="חזרה לדף הבית"
              >
                <span aria-hidden="true">{'←'}</span>
              </button>
              <h2 className="text-lg font-bold">כל העסקאות</h2>
            </div>
            <nav className="mt-2 flex gap-2 overflow-x-auto pb-1" aria-label="סינון לפי סוג עסקה">
              <button
                type="button"
                className="text-brand-primary-700 rounded-full bg-white px-3 py-1 text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
                aria-current="page"
              >
                הכל
              </button>
              <button
                type="button"
                className="text-brand-on-primary rounded-full bg-white/20 px-3 py-1 text-sm font-medium whitespace-nowrap transition-colors hover:bg-white/30 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
              >
                קופון
              </button>
              <button
                type="button"
                className="text-brand-on-primary rounded-full bg-white/20 px-3 py-1 text-sm font-medium whitespace-nowrap transition-colors hover:bg-white/30 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
              >
                קבוצתי
              </button>
            </nav>
          </header>
        </Stack>
      </ComponentEntry>

      {/* DealTypePicker */}
      <ComponentEntry
        name="DealTypePicker"
        id="dealtypepicker-domain"
        path="src/components/ui/domain/DealTypePicker"
        description="Four large RadioCards for deal type (FDS §5.4)."
        tokens={['color-brand-primary-50', 'color-brand-primary-600', 'radius-md']}
        a11yNotes={['RadioGroup keyboard nav', 'Each card has icon, label, description']}
        importSnippet={`import { DealTypePicker } from '@/components/ui/domain/DealTypePicker';`}
      >
        <DealTypePicker value={dealType} onChange={setDealType} />
      </ComponentEntry>

      {/* NotificationsToggleList */}
      <ComponentEntry
        name="NotificationsToggleList"
        id="notificationstogglelist-domain"
        path="src/components/ui/domain/NotificationsToggleList"
        description="List of Switch rows for notification preferences."
        tokens={['color-neutral-200', 'radius-md']}
        a11yNotes={[
          'Each Switch paired with <Label htmlFor>',
          'Silent-mode toggle separated visually',
        ]}
        importSnippet={`import { NotificationsToggleList } from '@/components/ui/domain/NotificationsToggleList';`}
      >
        <NotificationsToggleList
          items={notifItems}
          silentMode={silentMode}
          onSilentModeChange={setSilentMode}
        />
      </ComponentEntry>

      {/* HoursEditor */}
      <ComponentEntry
        name="HoursEditor"
        id="hourseditor-domain"
        path="src/components/ui/domain/HoursEditor"
        description="7-day hours grid with open/close inputs and a special-notes textarea."
        tokens={['radius-md', 'color-neutral-200']}
        a11yNotes={['Each day Switch labelled', 'Time inputs are real <input type=time>']}
        importSnippet={`import { HoursEditor } from '@/components/ui/domain/HoursEditor';`}
      >
        <HoursEditor value={hours} onChange={setHours} />
      </ComponentEntry>

      {/* ClubBadge */}
      <ComponentEntry
        name="ClubBadge"
        id="clubbadge-domain"
        path="src/components/ui/domain/ClubBadge"
        description="Pill showing club membership state."
        tokens={['color-success-50', 'color-neutral-100']}
        a11yNotes={['Decorative pill - text carries meaning']}
        importSnippet={`import { ClubBadge } from '@/components/ui/domain/ClubBadge';`}
      >
        <Row gap="2">
          <ClubBadge isMember />
          <ClubBadge isMember={false} />
        </Row>
      </ComponentEntry>

      {/* ClubHero */}
      <ComponentEntry
        name="ClubHero"
        id="clubhero-domain"
        path="src/components/ui/domain/ClubHero"
        description="Branded gradient hero banner for the /club page. Shows page title and tagline. Always rendered above CMS modules."
        tokens={[
          'color-brand-primary-50',
          'color-brand-primary-100',
          'color-text-primary',
          'color-text-secondary',
        ]}
        a11yNotes={[
          'Uses <header> landmark',
          'h1 inside — only use on pages where this is the sole h1',
        ]}
        importSnippet={`import { ClubHero } from '@/components/ui/domain/ClubHero';`}
      >
        <ClubHero />
      </ComponentEntry>

      {/* CategoryPillGroup */}
      <ComponentEntry
        name="CategoryPillGroup"
        id="categorypillgroup-domain"
        path="src/components/ui/domain/CategoryPillGroup"
        description="Single-select horizontal pill group for dynamic category lists."
        tokens={['color-brand-primary-600', 'color-neutral-200']}
        a11yNotes={['role=radiogroup on container', 'FilterChip aria-pressed semantics']}
        importSnippet={`import { CategoryPillGroup } from '@/components/ui/domain/CategoryPillGroup';`}
      >
        <CategoryPillGroup
          categories={[
            { id: '1', nameHe: 'מסעדות', nameEn: 'Restaurants' },
            { id: '2', nameHe: 'קפה', nameEn: 'Coffee' },
            { id: '3', nameHe: 'מאפים', nameEn: 'Bakery' },
          ]}
          selected="1"
          onSelect={() => null}
          locale="he"
        />
      </ComponentEntry>

      {/* TagPillGroup */}
      <ComponentEntry
        name="TagPillGroup"
        id="tagpillgroup-domain"
        path="src/components/ui/domain/TagPillGroup"
        description="Multi-select pill checkboxes for tag filtering."
        tokens={['color-brand-primary-600', 'color-neutral-200']}
        a11yNotes={['role=group on container', 'FilterChip aria-pressed semantics per tag']}
        importSnippet={`import { TagPillGroup } from '@/components/ui/domain/TagPillGroup';`}
      >
        <TagPillGroup
          tags={[
            { id: 't1', nameHe: 'כשר', nameEn: 'Kosher' },
            { id: 't2', nameHe: 'טבעוני', nameEn: 'Vegan' },
            { id: 't3', nameHe: 'ללא גלוטן', nameEn: 'Gluten-free' },
          ]}
          selected={['t1', 't3']}
          onToggle={() => null}
          locale="he"
          label="תגיות"
        />
      </ComponentEntry>

      {/* TagPillGroup — with collapse */}
      <ComponentEntry
        name="TagPillGroup (collapsed)"
        id="tagpillgroup-collapse-domain"
        path="src/components/ui/domain/TagPillGroup"
        description="TagPillGroup with collapseThreshold: shows top N pills, toggle button reveals rest."
        tokens={['color-mode-vendor-700']}
        a11yNotes={[
          'button[aria-expanded] on expand toggle',
          'role=group on visible pill container',
        ]}
        importSnippet={`import { TagPillGroup } from '@/components/ui/domain/TagPillGroup';`}
      >
        <TagPillGroup
          tags={[
            { id: 't1', nameHe: 'כשר', nameEn: 'Kosher' },
            { id: 't2', nameHe: 'טבעוני', nameEn: 'Vegan' },
            { id: 't3', nameHe: 'ללא גלוטן', nameEn: 'Gluten-free' },
            { id: 't4', nameHe: 'חלבי', nameEn: 'Dairy' },
            { id: 't5', nameHe: 'בשרי', nameEn: 'Meat' },
          ]}
          selected={['t1']}
          onToggle={() => null}
          locale="he"
          label="תגיות"
          collapseThreshold={2}
          seeAllLabel="See {count} more tags"
          showLessLabel="Show less"
        />
      </ComponentEntry>

      {/* VoucherCheckbox */}
      <ComponentEntry
        name="VoucherCheckbox"
        id="vouchercheckbox-domain"
        path="src/components/ui/domain/VoucherCheckbox"
        description="Checkbox to flag a deal as a voucher / store credit, with label and hint."
        tokens={['color-brand-primary-600', 'color-text-secondary']}
        a11yNotes={['Radix Checkbox with htmlFor Label', 'hint text below is plain <p>']}
        importSnippet={`import { VoucherCheckbox } from '@/components/ui/domain/VoucherCheckbox';`}
      >
        <VoucherCheckbox checked={false} onChange={() => null} />
      </ComponentEntry>

      {/* HeroCarousel */}
      <ComponentEntry
        name="HeroCarousel"
        id="herocarousel-domain"
        path="src/components/ui/domain/HeroCarousel"
        description="Auto-playing full-width featured carousel for desktop hero areas. RTL-aware slide direction. WCAG 2.2.2 pause/play always visible. prefers-reduced-motion stops auto-advance."
        tokens={[
          'color-glass-bg-light',
          'glass-blur-sm',
          'color-glass-border-white',
          'duration-slow',
          'ease-emphasized',
        ]}
        a11yNotes={[
          'Pause/play button always visible (WCAG 2.2.2) - not hover-only',
          'prefers-reduced-motion: no transition, no auto-advance',
          'visibilitychange: pauses when tab is hidden',
          'Dot indicators: aria-label + aria-current on each button',
          'Slide track: aria-live="polite" aria-atomic="false"',
          'RTL: translateX sign inverted when dir=rtl',
        ]}
        importSnippet={`import { HeroCarousel } from '@/components/ui/domain/HeroCarousel';`}
      >
        <div className="relative h-64 w-full overflow-hidden rounded-xl">
          <HeroCarousel
            slides={[
              {
                id: 'ds-slide-1',
                imageSrc:
                  'data:image/svg+xml,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 width%3D%22800%22 height%3D%22400%22%3E%3Crect fill%3D%22%231e40af%22 width%3D%22100%25%22 height%3D%22100%25%22%2F%3E%3C%2Fsvg%3E',
                imageAlt: 'Demo slide 1',
                label: 'Coffee Bean - קפה איכותי',
              },
              {
                id: 'ds-slide-2',
                imageSrc:
                  'data:image/svg+xml,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 width%3D%22800%22 height%3D%22400%22%3E%3Crect fill%3D%22%230369a1%22 width%3D%22100%25%22 height%3D%22100%25%22%2F%3E%3C%2Fsvg%3E',
                imageAlt: 'Demo slide 2',
                label: 'Pasta House - פסטה ביתית',
              },
            ]}
            mode="gallery"
          />
        </div>
      </ComponentEntry>
      {/* AvatarUploadButton */}
      <ComponentEntry
        name="AvatarUploadButton"
        id="avataruploadbutton-domain"
        path="src/components/ui/domain/AvatarUploadButton"
        description="Circular button that shows an avatar via the shared Image primitive (variant=thumb) or a User icon fallback. Triggers a hidden file input on click. Used in the profile screen for avatar upload."
        tokens={[
          'color-surface-inset',
          'color-surface-overlay',
          'color-text-muted',
          'color-text-inverse',
          'color-brand-primary-500',
          'radius-full',
        ]}
        a11yNotes={[
          'aria-label from i18n avatar_upload_button.change_avatar',
          'Hidden file input labelled with avatar_upload_button.file_input (sr-only)',
          'Camera overlay is aria-hidden (decorative)',
          'Focus ring always visible (focus-visible:ring-2)',
          'disabled state: button non-interactive + opacity-50',
        ]}
        importSnippet={`import { AvatarUploadButton } from '@/components/ui/domain/AvatarUploadButton';`}
      >
        <Row gap="6" align="center">
          {/* Empty state — User icon fallback */}
          <div className="flex flex-col items-center gap-2">
            <AvatarUploadButton alt="Empty avatar" onChange={() => {}} />
            <span className="text-text-muted text-xs">No image</span>
          </div>

          {/* With image */}
          <div className="flex flex-col items-center gap-2">
            <AvatarUploadButton
              src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64'%3E%3Crect fill='%231e40af' width='100%25' height='100%25'/%3E%3C/svg%3E"
              alt="Demo user avatar"
              onChange={() => {}}
            />
            <span className="text-text-muted text-xs">With image</span>
          </div>

          {/* Disabled */}
          <div className="flex flex-col items-center gap-2">
            <AvatarUploadButton alt="Disabled avatar" onChange={() => {}} disabled />
            <span className="text-text-muted text-xs">Disabled</span>
          </div>

          {/* Large (96px) */}
          <div className="flex flex-col items-center gap-2">
            <AvatarUploadButton alt="Large avatar" onChange={() => {}} size={96} />
            <span className="text-text-muted text-xs">size=96</span>
          </div>
        </Row>
      </ComponentEntry>

      {/* GroupProgressBar */}
      <ComponentEntry
        name="GroupProgressBar"
        id="groupprogressbar-domain"
        path="src/components/ui/domain/GroupProgressBar"
        description="Horizontal fill bar for group deal participation. Shows current vs min/max with milestone markers, tiered price markers, and participations_of text."
        tokens={[
          'color-brand-primary-500',
          'color-success-600',
          'color-surface-inset',
          'radius-full',
          'duration-slow',
        ]}
        a11yNotes={[
          'role="progressbar" with aria-valuenow, aria-valuemin, aria-valuemax',
          'aria-live="polite" on participant count text',
          'prefers-reduced-motion: fill transition disabled',
        ]}
        importSnippet={`import { GroupProgressBar } from '@/components/ui/domain/GroupProgressBar';`}
      >
        <Stack gap="6">
          <GroupProgressBar current={8} minTarget={15} maxTarget={30} />
          <GroupProgressBar current={17} minTarget={15} maxTarget={30} />
          <GroupProgressBar
            current={12}
            minTarget={15}
            maxTarget={30}
            tiers={[
              { minParticipants: 15, pricePerUnit: 89 },
              { minParticipants: 25, pricePerUnit: 69 },
            ]}
            showMilestones
          />
        </Stack>
      </ComponentEntry>

      {/* VendorCard */}
      <ComponentEntry
        name="VendorCard"
        id="vendorcard-domain"
        path="src/components/ui/domain/VendorCard"
        description="Public stores directory card. Shows hero image (16:9), logo avatar, business name, city, category pills, star rating, hours status, and active deals badge. Links to /business/{id}."
        tokens={[
          'color-surface-raised',
          'shadow-card',
          'shadow-card-hover',
          'radius-xl',
          'color-brand-primary-600',
          'color-success-700',
          'color-danger-600',
        ]}
        a11yNotes={[
          'Entire card is a single <a> link to /business/{id}',
          'Hero image uses alt="" aria-hidden (decorative)',
          'Logo image has alt={businessName}',
          'focus-visible:outline-2 ring on keyboard focus',
        ]}
        importSnippet={`import { VendorCard } from '@/components/ui/domain/VendorCard';`}
      >
        <Grid cols={2}>
          {(
            [
              {
                id: 'demo-1',
                businessName: 'קפה הנמל',
                displayName: 'Cafe HaNamal',
                logoUrl: null,
                heroImageUrl: null,
                reviewsScore: '4.7',
                reviewsCount: 38,
                city: 'תל אביב',
                lat: '32.0853',
                lng: '34.7818',
                businessTypeNames: ['בית קפה', 'מאפייה'],
                activeDealsCount: 2,
                activeDealsTypes: ['COUPON', 'GROUP'],
                todayHours: '08:00-22:00',
              },
              {
                id: 'demo-2',
                businessName: 'פיצה רומא',
                displayName: 'Pizza Roma',
                logoUrl: null,
                heroImageUrl: null,
                reviewsScore: '4.2',
                reviewsCount: 12,
                city: 'חיפה',
                lat: null,
                lng: null,
                businessTypeNames: ['מסעדה'],
                activeDealsCount: 0,
                activeDealsTypes: [],
                todayHours: 'closed',
              },
            ] satisfies VendorCardData[]
          ).map((v) => (
            <VendorCard key={v.id} vendor={v} locale="he" />
          ))}
        </Grid>
      </ComponentEntry>

      {/* IsraeliAddressField */}
      <ComponentEntry
        name="IsraeliAddressField"
        id="israeliaddressfield-domain"
        path="src/components/ui/domain/IsraeliAddressField"
        description="Structured Israeli address picker. City dropdown from data.gov.il (~1500 cities), street dropdown filtered by city (~3000 streets), house number + apt free-form. Triggers Nominatim geocoding on house number blur and shows an OSM iframe preview when coordinates are resolved."
        tokens={['color-warning-700', 'color-text-secondary', 'spacing-3', 'font-size-sm']}
        a11yNotes={[
          'Each field has a visible Label with htmlFor',
          'Street Select is aria-disabled until city is selected',
          'OSM iframe has title attribute',
          'Geocoding error shown as role="alert"',
        ]}
        importSnippet={`import { IsraeliAddressField } from '@/components/ui/domain/IsraeliAddressField';`}
      >
        <IsraeliAddressField value={null} onChange={() => {}} />
      </ComponentEntry>

      {/* VendorHeroPreview */}
      <ComponentEntry
        name="VendorHeroPreview"
        id="vendorheropreview-domain"
        path="src/components/ui/domain/VendorHeroPreview"
        description="Live preview of vendor hero banner (hero image + logo + name). Shows gradient overlay on image with focal point applied via object-position. Fallback: solid brand gradient when no image."
        tokens={['color-brand-primary-700', 'text-text-on-brand', 'radius-lg', 'shadow-md']}
        a11yNotes={[
          'Hero image is aria-hidden (decorative)',
          'Logo image is aria-hidden (decorative)',
          'Vendor name is visible text label',
          'No interactive content — preview only',
        ]}
        importSnippet={`import { VendorHeroPreview } from '@/components/ui/domain/VendorHeroPreview';`}
      >
        <Stack gap="4">
          <VendorHeroPreview
            heroImageUrl="data:image/svg+xml,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 width%3D%221600%22 height%3D%22900%22%3E%3Crect fill%3D%22%23d1d5db%22 width%3D%22100%25%22 height%3D%22100%25%22%2F%3E%3C%2Fsvg%3E"
            logoUrl={null}
            vendorName="ספא מרב"
            focalX={0.5}
            focalY={0.3}
          />
          <VendorHeroPreview heroImageUrl={null} logoUrl={null} vendorName="עסק ללא תמונה" />
        </Stack>
      </ComponentEntry>

      {/* ─── Cart components (W1-B) ─────────────────────────────────────────── */}

      {/* QuantityStepper */}
      <ComponentEntry
        name="QuantityStepper"
        id="quantitystepper-primitive"
        path="src/components/ui/primitives/QuantityStepper"
        description="Discrete +/- stepper for cart quantity. No text input — display-only count between two icon buttons. Disables at bounds."
        tokens={[
          'border-border-default',
          'bg-surface-base',
          'bg-surface-hover',
          'text-text-secondary',
          'text-text-primary',
          'brand-primary-500',
          'duration-fast',
        ]}
        a11yNotes={[
          'role=group with aria-label from label prop',
          'Decrement/increment buttons have aria-label via i18n (cart.qty_decrease / cart.qty_increase)',
          'Count span: role=spinbutton, aria-valuenow/min/max, keyboard ArrowUp/ArrowDown',
          'Buttons disabled at bounds — cursor-not-allowed + opacity',
          'Focus-visible ring on buttons and count span',
        ]}
        importSnippet={`import { QuantityStepper } from '@/components/ui/primitives/QuantityStepper';`}
      >
        <div className="flex flex-col gap-4">
          <div className="flex items-center gap-4">
            <QuantityStepper
              value={stepperQty}
              min={1}
              max={5}
              onChange={setStepperQty}
              label="ארוחת בוקר"
            />
            <span className="text-text-muted text-sm">min=1 max=5 (interactive)</span>
          </div>
          <div className="flex items-center gap-4">
            <QuantityStepper value={1} min={1} max={1} onChange={() => {}} label="at both bounds" />
            <span className="text-text-muted text-sm">Both bounds disabled</span>
          </div>
          <div className="flex items-center gap-4">
            <QuantityStepper
              value={3}
              min={1}
              max={5}
              onChange={() => {}}
              label="disabled"
              disabled
            />
            <span className="text-text-muted text-sm">Disabled state</span>
          </div>
        </div>
      </ComponentEntry>

      <ComponentEntry
        name="AnimatedNumber"
        id="animatednumber-domain"
        path="src/components/ui/domain/AnimatedNumber"
        description="Motion primitive for count and money readouts. Reserves width to prevent layout shift, formats every intermediate frame, and snaps immediately under reduced motion."
        tokens={['text-text-primary', 'text-text-secondary', 'duration-slow', 'brand-primary-600']}
        a11yNotes={[
          'Readout can expose aria-label when used outside plain text flow',
          'Width probe is aria-hidden and only reserves layout',
          'Reduced motion skips the tween and renders the final value immediately',
        ]}
        importSnippet={`import { AnimatedNumber } from '@/components/ui/domain/AnimatedNumber';`}
      >
        <div className="flex max-w-2xl flex-col gap-4">
          <div className="border-border-default bg-surface-base flex items-center justify-between rounded-2xl border p-4">
            <div className="flex flex-col gap-1">
              <span className="text-text-secondary text-xs">Cart count</span>
              <AnimatedNumber
                value={animatedCount}
                reserveValue={99}
                className="text-text-primary text-2xl font-bold"
                ariaLabel="Animated cart count"
              />
            </div>
            <div className="flex flex-col items-end gap-1">
              <span className="text-text-secondary text-xs">Order total</span>
              <AnimatedNumber
                value={animatedMoney}
                reserveValue={99900}
                className="text-text-primary text-2xl font-bold"
                formatValue={(raw) => formatAgorotWhole(Math.round(raw))}
                ariaLabel="Animated order total"
                testId="animated-number-money"
              />
            </div>
          </div>

          <div className="border-border-default bg-surface-subtle flex items-center justify-between rounded-2xl border p-4">
            <CartIconButton count={animatedCount} onClick={() => {}} />
            <DealCTA
              deal={{
                id: 'animated-number-demo-sku',
                title: 'Motion deal',
                discountedPrice: animatedMoney,
              }}
              mode="add"
              size="md"
            />
          </div>

          <div className="flex gap-3">
            <Button
              variant="secondary"
              size="sm"
              onClick={() => {
                setAnimatedCount((value) => (value >= 11 ? 2 : value + 4));
                setAnimatedMoney((value) => (value >= 18900 ? 8900 : value + 5000));
              }}
            >
              Animate totals
            </Button>
          </div>
        </div>
      </ComponentEntry>

      {/* CartIconButton */}
      <ComponentEntry
        name="CartIconButton"
        id="carticonbutton-domain"
        path="src/components/ui/domain/cart/CartIconButton"
        description="Shopping cart nav button with badge overlay. Renders badge only when count > 0; caps at 9+."
        tokens={[
          'brand-primary-600',
          'brand-on-primary',
          'bg-surface-hover',
          'text-text-secondary',
          'text-text-primary',
          'duration-fast',
        ]}
        a11yNotes={[
          'aria-label includes count via badgeLabel i18n key (with {count} replaced)',
          'Badge digit is aria-hidden',
          'focus-visible ring on button',
          'ShoppingCart icon is aria-hidden (decorative)',
        ]}
        importSnippet={`import { CartIconButton } from '@/components/ui/domain/cart/CartIconButton';`}
      >
        <div className="flex flex-wrap items-center gap-6">
          <div className="flex flex-col items-center gap-1">
            <CartIconButton count={0} onClick={() => {}} />
            <span className="text-text-muted text-xs">count=0 (no badge)</span>
          </div>
          <div className="flex flex-col items-center gap-1">
            <CartIconButton count={3} onClick={() => {}} />
            <span className="text-text-muted text-xs">count=3</span>
          </div>
          <div className="flex flex-col items-center gap-1">
            <CartIconButton count={9} onClick={() => {}} />
            <span className="text-text-muted text-xs">count=9</span>
          </div>
          <div className="flex flex-col items-center gap-1">
            <CartIconButton count={12} onClick={() => {}} />
            <span className="text-text-muted text-xs">count=12 → &quot;9+&quot;</span>
          </div>
        </div>
      </ComponentEntry>

      {/* CartLineItem */}
      <ComponentEntry
        name="CartLineItem"
        id="cartlineitem-domain"
        path="src/components/ui/domain/cart/CartLineItem"
        description="One row in the cart: deal image, title, vendor, unit price, QuantityStepper, remove button."
        tokens={[
          'text-text-primary',
          'text-text-muted',
          'brand-primary-700',
          'danger-50',
          'danger-600',
          'border-border-subtle',
          'duration-fast',
        ]}
        a11yNotes={[
          'Remove button has aria-label from cart.remove i18n key',
          'QuantityStepper label is deal title',
          'Image alt = deal title',
          'Disabled state sets pointer-events-none + opacity-60',
          'Trash icon is aria-hidden',
        ]}
        importSnippet={`import { CartLineItem } from '@/components/ui/domain/cart/CartLineItem';`}
      >
        <div className="divide-border-subtle border-border-default max-w-sm divide-y rounded-lg border">
          {SAMPLE_CART_ITEMS.map((item) => (
            <CartLineItem
              key={item.dealId}
              item={{ ...item, qty: item.dealId === 'demo-deal-1' ? cartQty : item.qty }}
              onQtyChange={(dealId, qty) => {
                if (dealId === 'demo-deal-1') setCartQty(qty);
              }}
              onRemove={() => {}}
            />
          ))}
        </div>
      </ComponentEntry>

      {/* CartEmptyState */}
      <ComponentEntry
        name="CartEmptyState"
        id="cartemptystate-domain"
        path="src/components/ui/domain/cart/CartEmptyState"
        description="Empty cart illustration + heading + CTA. Illustration slot accepts custom ReactNode; defaults to ShoppingCart icon."
        tokens={['bg-surface-subtle', 'text-text-muted', 'text-text-primary']}
        a11yNotes={[
          'ShoppingCart icon is aria-hidden (decorative)',
          'Browse CTA is a Button (keyboard accessible)',
          'No interactive content when onBrowse is omitted',
        ]}
        importSnippet={`import { CartEmptyState } from '@/components/ui/domain/cart/CartEmptyState';`}
      >
        <div className="flex gap-8">
          <div className="border-border-default rounded-lg border p-4">
            <CartEmptyState onBrowse={() => {}} />
            <p className="text-text-muted mt-2 text-center text-xs">With CTA</p>
          </div>
          <div className="border-border-default rounded-lg border p-4">
            <CartEmptyState />
            <p className="text-text-muted mt-2 text-center text-xs">Without CTA</p>
          </div>
        </div>
      </ComponentEntry>

      {/* CartStaleToast */}
      <ComponentEntry
        name="CartStaleToast"
        id="cartstaletoast-domain"
        path="src/components/ui/domain/cart/CartStaleToast"
        description="Toast body listing removed cart items with reason chips (expired / sold-out). Renders null when removedItems is empty."
        tokens={[
          'text-text-primary',
          'warning-500 (Pill tone=warning)',
          'danger-500 (Pill tone=danger)',
        ]}
        a11yNotes={[
          'Reason chips use Pill (semantic <span> with tone)',
          'Text content is plain text, no raw HTML',
          'Wrapping in Toast overlay is caller responsibility (W2-B)',
        ]}
        importSnippet={`import { CartStaleToast } from '@/components/ui/domain/cart/CartStaleToast';`}
      >
        <div className="border-border-default max-w-sm rounded-lg border p-4">
          <CartStaleToast removedItems={SAMPLE_REMOVED_ITEMS} />
        </div>
      </ComponentEntry>

      {/* CartDrawer */}
      <ComponentEntry
        name="CartDrawer"
        id="cartdrawer-domain"
        path="src/components/ui/domain/cart/CartDrawer"
        description="End-anchored sheet (Radix Dialog, side=end) showing cart items, subtotal, and View Cart / Checkout CTAs. RTL-safe via Drawer primitive."
        tokens={[
          'z-modal (via Drawer)',
          'bg-surface-base (via Drawer)',
          'border-border-subtle (dividers)',
          'text-text-primary / text-text-secondary',
          'brand-primary-600 (Checkout CTA)',
          'shadow-xl (via Drawer)',
        ]}
        a11yNotes={[
          'role=dialog with aria-labelledby (DrawerTitle = Radix Dialog.Title)',
          'Focus trap: Tab cycles inside; Esc closes (Radix built-in)',
          'Close button aria-label from common.close',
          'CartLineItem list uses <ul role=list> + <li>',
          'prefers-reduced-motion: fade-only via Drawer (motion-reduce:animate-none)',
          'RTL: side=end resolves to inset-inline-end anchor; slide direction flips per [dir=rtl] class',
        ]}
        importSnippet={`import { CartDrawer } from '@/components/ui/domain/cart/CartDrawer';`}
      >
        <div className="flex flex-col gap-3">
          <Button variant="secondary" size="sm" onClick={() => setCartDrawerOpen(true)}>
            Open CartDrawer (with items)
          </Button>
          <CartDrawer
            open={cartDrawerOpen}
            onOpenChange={setCartDrawerOpen}
            items={SAMPLE_CART_ITEMS}
            subtotal={15900}
            onViewCart={() => setCartDrawerOpen(false)}
            onCheckout={() => setCartDrawerOpen(false)}
            onQtyChange={(dealId, qty) => {
              if (dealId === 'demo-deal-1') setCartQty(qty);
            }}
            onRemove={() => {}}
          />
        </div>
      </ComponentEntry>

      {/* ── CartNavButton (W2-B) ───────────────────────────────────────────── */}
      <ComponentEntry
        name="CartNavButton"
        id="cartnavbutton-domain"
        path="src/components/ui/domain/cart/CartNavButton"
        description="Wiring layer: connects CartIconButton to the global cartDrawerStore. Drop into navbar endSlot. Uses zustand anon store for count — no QueryClientProvider required."
        tokens={['CartIconButton', 'cartDrawerStore', 'useAnonCart']}
        a11yNotes={[
          'Inherits CartIconButton aria-label (count-aware)',
          'No QueryClientProvider dependency — safe in any island',
        ]}
        importSnippet={`import { CartNavButton } from '@/components/ui/domain/cart/CartNavButton';`}
      >
        <div className="flex gap-4">
          <CartNavButton />
        </div>
      </ComponentEntry>

      {/* ── Vendor pending/rejected screens ────────────────────────────────── */}
      <VendorPendingRejectedSection />

      {/* ── Admin domain components ────────────────────────────────────────── */}
      <AdminDomainSection />

      {/* ── Near You components (Wave 3) ───────────────────────────────────── */}
      <NearYouSection />

      {/* ── StatusBadge (generic state→Pill) ───────────────────────────────── */}
      <ComponentEntry
        name="StatusBadge"
        id="statusbadge-domain"
        path="src/components/ui/domain/StatusBadge"
        description="Generic state→Pill renderer. Domain status badges collapse to a `map` config (tone + i18n labelKey) passed here instead of bespoke components."
        tokens={['color-success-50', 'color-warning-50', 'color-danger-50', 'color-info-50']}
        a11yNotes={['Label text is translated, never tone-only', 'Inherits Pill contrast tokens']}
        importSnippet={`import { StatusBadge } from '@/components/ui/domain/StatusBadge';`}
      >
        <Row gap="2" wrap>
          <StatusBadge state="requested" map={STATUS_BADGE_DEMO_MAP} ns="returns" />
          <StatusBadge state="approved" map={STATUS_BADGE_DEMO_MAP} ns="returns" />
          <StatusBadge state="refunded" map={STATUS_BADGE_DEMO_MAP} ns="returns" />
          <StatusBadge state="denied" map={STATUS_BADGE_DEMO_MAP} ns="returns" />
        </Row>
      </ComponentEntry>
    </SectionShell>
  );
}

// ─── Vendor pending/rejected section ─────────────────────────────────────────

import { VendorPendingActiveDashboard } from '@/features/vendor-welcome/VendorPendingActiveDashboard';
import { VendorRejectedScreen } from '@/features/vendor-welcome/VendorRejectedScreen';

function VendorPendingRejectedSection() {
  return (
    <Stack gap="6">
      {/* VendorPendingActiveDashboard — PENDING state */}
      <ComponentEntry
        name="VendorPendingActiveDashboard (PENDING)"
        id="vendor-pending-active-dashboard-pending"
        path="src/features/vendor-welcome/VendorPendingActiveDashboard"
        description="Functional dashboard shown while vendor account is PENDING. Status banner (info tone), profile placeholder, deal placeholder with disabled CTA + tooltip."
        tokens={[
          'color-info-50',
          'color-surface-raised',
          'color-border-subtle',
          'spacing-4',
          'font-size-base',
        ]}
        a11yNotes={[
          'h1 heading (sr-only) provides accessible page label',
          'InlineNotice uses role=status for info tone',
          'Disabled button wrapped in span so Tooltip still triggers',
          'All strings via useT vendor_welcome namespace',
        ]}
        importSnippet={`import { VendorPendingActiveDashboard } from '@/features/vendor-welcome/VendorPendingActiveDashboard';`}
      >
        <div className="border-border-default overflow-hidden rounded-lg border">
          <VendorPendingActiveDashboard
            state="PENDING_FIRST_APPROVAL"
            businessName="הקפה של דנה"
            flagReason={null}
            demo
          />
        </div>
      </ComponentEntry>

      {/* VendorPendingActiveDashboard — PENDING_PROCESSOR state (with flagReason) */}
      <ComponentEntry
        name="VendorPendingActiveDashboard (PENDING_PROCESSOR + flagReason)"
        id="vendor-pending-active-dashboard-processor"
        path="src/features/vendor-welcome/VendorPendingActiveDashboard"
        description="PENDING_PROCESSOR variant: warning tone banner + prominent vendor-green 'Complete payment setup' CTA. Also shows 'under manual review' sub-line when flagReason is set. CTA links to /vendor/settings/payments where StripeOnboarding renders."
        tokens={['color-warning-50', 'color-mode-vendor-600', 'spacing-4']}
        a11yNotes={[
          'InlineNotice uses role=alert for warning tone',
          'Payment CTA is a full-width vendor Button',
        ]}
        importSnippet={`import { VendorPendingActiveDashboard } from '@/features/vendor-welcome/VendorPendingActiveDashboard';`}
      >
        <div className="border-border-default overflow-hidden rounded-lg border">
          <VendorPendingActiveDashboard
            state="PENDING_PROCESSOR"
            businessName="פיצה רומא"
            flagReason="Incomplete business registration"
            demo
          />
        </div>
      </ComponentEntry>

      {/* VendorRejectedScreen */}
      <ComponentEntry
        name="VendorRejectedScreen"
        id="vendor-rejected-screen"
        path="src/features/vendor-welcome/VendorRejectedScreen"
        description="Full-page rejection notice. Danger icon, h1 title, optional rejectReason, and a mailto support link."
        tokens={['color-danger-100', 'color-danger-600', 'spacing-5', 'font-size-2xl']}
        a11yNotes={[
          'h1 has id for aria-labelledby on main landmark',
          'Support link is a real <a href="mailto:…"> — keyboard reachable',
          'AlertCircle icon is decorative: wrapped in aria-hidden span',
        ]}
        importSnippet={`import { VendorRejectedScreen } from '@/features/vendor-welcome/VendorRejectedScreen';`}
      >
        <div className="border-border-default overflow-hidden rounded-lg border">
          <VendorRejectedScreen rejectReason="לא עמד בדרישות הרישוי" />
        </div>
      </ComponentEntry>
    </Stack>
  );
}

// ─── Admin domain section ─────────────────────────────────────────────────────

const SAMPLE_ADMIN_ROWS: Array<{ id: string; name: string; state: string; purchases: number }> = [
  { id: '1', name: 'Dana Levi', state: 'active', purchases: 12 },
  { id: '2', name: 'Yossi Cohen', state: 'frozen', purchases: 3 },
  { id: '3', name: 'Rina Mizrahi', state: 'active', purchases: 45 },
];

function AdminDomainSection() {
  const [page, setPage] = useState(1);

  return (
    <Stack gap="6">
      {/* AdminBackLink */}
      <ComponentEntry
        name="AdminBackLink"
        id="adminbacklink-domain"
        path="src/components/ui/domain/admin/AdminBackLink"
        description="Standardised back-navigation link for admin detail pages. Shows a ChevronRight icon (auto-mirrors in RTL) and a resolved label. Falls back to common.back when no label is provided."
        tokens={['color-text-secondary', 'color-text-primary', 'duration-fast']}
        a11yNotes={[
          'Semantic <a> element — keyboard reachable by default',
          'Focus ring via focus-visible:ring-brand-primary-500',
          'Icon is aria-hidden (decorative)',
          'ChevronRight auto-mirrors in RTL via mirror prop',
        ]}
        importSnippet={`import { AdminBackLink } from '@/components/ui/domain/admin/AdminBackLink';`}
      >
        <Stack gap="3">
          <AdminBackLink href="/admin/vendors" />
          <AdminBackLink href="/admin/deals" label="Back to deals" />
        </Stack>
      </ComponentEntry>

      {/* Pagination */}
      <ComponentEntry
        name="Pagination"
        id="pagination-domain"
        path="src/components/ui/primitives/Pagination"
        description="URL-param driven prev/next + page number strip. Renders nothing when totalPages ≤ 1."
        tokens={['color-brand-primary-600', 'color-text-secondary']}
        a11yNotes={[
          'nav element with aria-label',
          'aria-current=page on active page',
          'Disabled prev/next via disabled prop on Button',
        ]}
        importSnippet={`import { Pagination } from '@/components/ui/primitives/Pagination';`}
      >
        <Stack gap="4">
          <Pagination page={page} totalPages={10} onPageChange={setPage} />
          <Pagination page={1} totalPages={3} onPageChange={() => {}} />
        </Stack>
      </ComponentEntry>

      {/* AdminFilterBar */}
      <ComponentEntry
        name="AdminFilterBar"
        id="adminfilterbar-domain"
        path="src/components/ui/domain/admin/AdminFilterBar"
        description="Filter row with search input, chip toggles, and date range. All state is URL-param driven via useAdminFilters."
        tokens={['color-surface-raised', 'color-border-default', 'color-brand-primary-600']}
        a11yNotes={[
          'Search input has visible Label (sr-only)',
          'Chip buttons use aria-pressed',
          'Date inputs have visible Labels',
          'useAdminFilters is SSR-safe via useSyncExternalStore',
        ]}
        importSnippet={`import { AdminFilterBar } from '@/components/ui/domain/admin/AdminFilterBar';`}
      >
        <AdminFilterBar
          searchKey="search"
          chips={[
            { key: 'state', value: 'active', label: 'Active' },
            { key: 'state', value: 'frozen', label: 'Frozen' },
          ]}
          dateRange={{ fromKey: 'from', toKey: 'to' }}
        />
      </ComponentEntry>

      {/* AdminTable */}
      <ComponentEntry
        name="AdminTable"
        id="admintable-domain"
        path="src/components/ui/domain/admin/AdminTable"
        description="Generic data table. Row click navigates to rowHref via an ::after overlay on the first-cell anchor. Actions column uses relative z-index to stay above the overlay."
        tokens={['color-border-default', 'color-surface-raised', 'color-brand-primary-600']}
        a11yNotes={[
          'thead/tbody/th/td with scope=col',
          'Row link is a real <a> with ::after overlay; tr is not interactive',
          'Actions are <button> elements with z-index above the overlay',
          'Loading state has aria-live=polite aria-busy=true',
          'Empty state uses EmptyState component',
        ]}
        importSnippet={`import { AdminTable } from '@/components/ui/domain/admin/AdminTable';`}
      >
        <Stack gap="4">
          <AdminTable
            columns={[
              { key: 'name', label: 'Name', render: (r) => r.name },
              { key: 'state', label: 'State', align: 'center' },
              { key: 'purchases', label: 'Purchases', align: 'end' },
            ]}
            rows={SAMPLE_ADMIN_ROWS}
            rowHref={(r) => `/admin/users/${r.id}`}
            actions={[
              {
                key: 'freeze',
                label: 'Freeze',
                onAction: () => {},
                hidden: (r) => r.state === 'frozen',
              },
              {
                key: 'unfreeze',
                label: 'Unfreeze',
                onAction: () => {},
                hidden: (r) => r.state !== 'frozen',
              },
            ]}
          />
          <AdminTable columns={[{ key: 'name', label: 'Name' }]} rows={[]} />
        </Stack>
      </ComponentEntry>

      {/* AdminListPage */}
      <ComponentEntry
        name="AdminListPage"
        id="adminlistpage-domain"
        path="src/components/ui/domain/admin/AdminListPage"
        description="Full list page shell: header, filter bar slot, table slot, pagination slot. Handles loading/error/idle states."
        tokens={['color-surface-base', 'color-border-default', 'shadow-sm', 'radius-xl']}
        a11yNotes={[
          'h1 page heading',
          'Loading: aria-live=polite aria-busy=true',
          'Error: ErrorState with optional retry action',
          'Primary action slot for CTA buttons',
        ]}
        importSnippet={`import { AdminListPage } from '@/components/ui/domain/admin/AdminListPage';`}
      >
        <Stack gap="4">
          <div className="border-border-default overflow-hidden rounded-lg border">
            <AdminListPage
              subtitle="Manage platform users"
              status="idle"
              filterBar={
                <AdminFilterBar
                  searchKey="search"
                  chips={[
                    { key: 'state', value: 'active', label: 'Active' },
                    { key: 'state', value: 'frozen', label: 'Frozen' },
                  ]}
                />
              }
              table={
                <AdminTable
                  columns={[
                    { key: 'name', label: 'Name', render: (r) => r.name },
                    { key: 'state', label: 'State', align: 'center' },
                  ]}
                  rows={SAMPLE_ADMIN_ROWS}
                />
              }
              pagination={<Pagination page={1} totalPages={5} onPageChange={() => {}} />}
            />
          </div>
          <div className="border-border-default flex h-40 items-center justify-center overflow-hidden rounded-lg border">
            <AdminListPage status="loading" />
          </div>
        </Stack>
      </ComponentEntry>

      {/* AdminDetailPage */}
      <ComponentEntry
        name="AdminDetailPage"
        id="admindetailpage-domain"
        path="src/components/ui/domain/admin/AdminDetailPage"
        description="Detail page layout: sticky summary card on start + tabbed panel on end (desktop). Stacked on mobile. Tab state driven by URL hash."
        tokens={['color-border-default', 'color-brand-primary-600', 'color-brand-primary-700']}
        a11yNotes={[
          'Tab buttons: role=tab, aria-controls, aria-selected',
          'Tab panels: role=tabpanel, aria-labelledby, hidden',
          'Active tab panel: tabIndex=0 for keyboard focus',
          'URL hash drives active tab — deep-linking works',
          'Keyboard: Tab moves between tabs, Enter/Space activates',
        ]}
        importSnippet={`import { AdminDetailPage } from '@/components/ui/domain/admin/AdminDetailPage';`}
      >
        <div className="border-border-default min-h-64 overflow-hidden rounded-lg border">
          <AdminDetailPage
            summary={
              <div className="bg-surface-raised border-border-default rounded-xl border p-4">
                <p className="text-text-primary font-bold">Dana Levi</p>
                <p className="text-text-secondary text-sm">User #1</p>
              </div>
            }
            tabs={[
              {
                key: 'profile',
                label: 'Profile',
                content: <p className="text-text-secondary p-4 text-sm">Profile content here</p>,
              },
              {
                key: 'purchases',
                label: 'Purchases',
                content: <p className="text-text-secondary p-4 text-sm">Purchase history here</p>,
              },
              {
                key: 'activity',
                label: 'Activity',
                content: <p className="text-text-secondary p-4 text-sm">Activity log here</p>,
              },
            ]}
            defaultTab="profile"
          />
        </div>
      </ComponentEntry>

      {/* CategoriesTabsNav */}
      <ComponentEntry
        name="CategoriesTabsNav"
        id="categoriestabsnav-domain"
        path="src/components/ui/layout/CategoriesTabsNav"
        description="Horizontal tab strip for the /admin/categories/* namespace. Two anchor tabs: categories | tags. Active tab uses brand-primary underline. RTL-first, no hardcoded colors."
        tokens={[
          'color-brand-primary-600',
          'color-brand-primary-700',
          'color-border-default',
          'color-text-secondary',
          'color-text-primary',
          'color-surface-base',
        ]}
        a11yNotes={[
          'nav element with aria-label',
          'role="tablist" on ul, role="tab" on each anchor',
          'aria-selected="true" on active tab',
          'Keyboard-navigable anchors',
          'focus-visible outline with brand-primary-500',
        ]}
        importSnippet={`import { CategoriesTabsNav } from '@/components/ui/layout/CategoriesTabsNav';\n// Usage: <CategoriesTabsNav current="categories" locale="he" />`}
      >
        <Stack gap="4">
          <div>
            <p className="text-text-muted mb-2 text-sm">
              current=&quot;categories&quot; locale=&quot;he&quot;
            </p>
            <CategoriesTabsNav current="categories" locale="he" />
          </div>
          <div>
            <p className="text-text-muted mb-2 text-sm">
              current=&quot;tags&quot; locale=&quot;he&quot;
            </p>
            <CategoriesTabsNav current="tags" locale="he" />
          </div>
          <div>
            <p className="text-text-muted mb-2 text-sm">
              current=&quot;categories&quot; locale=&quot;en&quot;
            </p>
            <CategoriesTabsNav current="categories" locale="en" />
          </div>
          <div>
            <p className="text-text-muted mb-2 text-sm">
              current=&quot;tags&quot; locale=&quot;en&quot;
            </p>
            <CategoriesTabsNav current="tags" locale="en" />
          </div>
        </Stack>
      </ComponentEntry>

      {/* ─── DealCTA ─────────────────────────────────────────────────────── */}
      <ComponentEntry
        name="DealCTA"
        id="deal-cta-domain"
        path="src/components/ui/domain/DealCTA"
        description="Buy / add-to-cart CTA for deal surfaces. Modes: buy, add, both. Sizes: sm (icon-only), md (icon+short label), lg (full-width). soldOut disables."
        tokens={['brand-primary-600', 'brand-on-primary', 'neutral-200', 'neutral-500']}
        a11yNotes={[
          'Icon-only buttons have aria-label',
          'Sold-out state uses disabled attribute',
          'Toast on add-to-cart announced via aria-live',
        ]}
        importSnippet={`import { DealCTA } from '@/components/ui/domain/DealCTA';`}
      >
        <Stack gap="4">
          <div>
            <p className="text-text-secondary mb-2 text-xs font-medium">
              mode=buy, sizes sm / md / lg
            </p>
            <Row gap="3" wrap>
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="sm"
                mode="buy"
              />
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="md"
                mode="buy"
              />
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="lg"
                mode="buy"
              />
            </Row>
          </div>
          <div>
            <p className="text-text-secondary mb-2 text-xs font-medium">
              mode=add, sizes sm / md / lg
            </p>
            <Row gap="3" wrap>
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="sm"
                mode="add"
              />
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="md"
                mode="add"
              />
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="lg"
                mode="add"
              />
            </Row>
          </div>
          <div>
            <p className="text-text-secondary mb-2 text-xs font-medium">mode=both</p>
            <Stack gap="2">
              <Row gap="3" wrap>
                <DealCTA
                  deal={{
                    id: 'ds-1',
                    title: 'ארוחת בורגר',
                    vendorName: 'מסעדת רוטשילד 22',
                    discountedPrice: 45,
                  }}
                  size="sm"
                  mode="both"
                />
                <DealCTA
                  deal={{
                    id: 'ds-1',
                    title: 'ארוחת בורגר',
                    vendorName: 'מסעדת רוטשילד 22',
                    discountedPrice: 45,
                  }}
                  size="md"
                  mode="both"
                />
              </Row>
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="lg"
                mode="both"
              />
            </Stack>
          </div>
          <div>
            <p className="text-text-secondary mb-2 text-xs font-medium">soldOut=true</p>
            <Row gap="3" wrap>
              <DealCTA
                deal={{
                  id: 'ds-sold',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="sm"
                mode="buy"
                soldOut
              />
              <DealCTA
                deal={{
                  id: 'ds-sold',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="md"
                mode="buy"
                soldOut
              />
              <DealCTA
                deal={{
                  id: 'ds-sold',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="lg"
                mode="both"
                soldOut
              />
            </Row>
          </div>
          <div>
            <p className="text-text-secondary mb-2 text-xs font-medium">
              iconOnly=true (md override)
            </p>
            <Row gap="3" wrap>
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="md"
                mode="buy"
                iconOnly
              />
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="md"
                mode="add"
                iconOnly
              />
              <DealCTA
                deal={{
                  id: 'ds-1',
                  title: 'ארוחת בורגר',
                  vendorName: 'מסעדת רוטשילד 22',
                  discountedPrice: 45,
                }}
                size="md"
                mode="both"
                iconOnly
              />
            </Row>
          </div>
        </Stack>
      </ComponentEntry>

      {/* DraftsList */}
      <ComponentEntry
        name="DraftsList"
        id="draftslist-domain"
        path="src/components/ui/domain/DraftsList"
        description="TanStack Query island that fetches and renders vendor deal drafts. Starred drafts appear first (server-sorted). Supports star toggle, delete (with confirm), and resume navigation."
        tokens={[
          'color-surface-raised',
          'color-border-default',
          'color-text-secondary',
          'color-danger-600',
          'color-warning-400',
        ]}
        a11yNotes={[
          'List rendered as <ul> with <li> wrappers; aria-busy on loading state',
          'Error state uses role="alert"',
          'Empty state CTA is an <a> element (navigates)',
          'All DraftDealCard actions are keyboard-accessible <button> elements',
        ]}
        importSnippet={`import { DraftsList } from '@/components/ui/domain/DraftsList';`}
      >
        {(() => {
          const mockDrafts: DraftSummary[] = [
            {
              id: 'ds-mock-1',
              title: 'ארוחת בוקר מיוחדת',
              updatedAt: '2026-04-24T11:55:00.000Z',
              isStarred: true,
            },
            {
              id: 'ds-mock-2',
              title: null,
              updatedAt: '2026-04-24T10:00:00.000Z',
              isStarred: false,
            },
            {
              id: 'ds-mock-3',
              title: 'עוגת שוקולד פרמיום',
              updatedAt: '2026-04-23T12:00:00.000Z',
              isStarred: false,
            },
          ];
          return (
            <ul className="flex flex-col gap-3">
              {mockDrafts.map((draft) => (
                <li key={draft.id}>
                  <DraftDealCard
                    id={draft.id}
                    title={draft.title}
                    updatedAt={draft.updatedAt}
                    isStarred={draft.isStarred}
                    onToggleStar={() => {}}
                    onResume={() => {}}
                    onDelete={() => {}}
                  />
                </li>
              ))}
            </ul>
          );
        })()}
      </ComponentEntry>

      {/* VendorKpiTile — P4 flush */}
      <ComponentEntry
        name="VendorKpiTile"
        id="vendorkpitile-domain"
        path="src/components/ui/domain/vendor/VendorKpiTile"
        description="KPI metric tile for the vendor dashboard. Shows label, value, and delta trend arrow tinted by deltaVariant."
        tokens={[
          'color-surface-raised',
          'color-border-subtle',
          'color-text-primary',
          'color-text-muted',
          'color-success-600',
          'color-warning-600',
          'color-danger-600',
        ]}
        a11yNotes={['article element with aria-label={label}', 'delta arrow aria-hidden']}
        importSnippet={`import { VendorKpiTile } from '@/components/ui/domain/vendor/VendorKpiTile';`}
      >
        <Stack gap="4">
          <VendorKpiTile label="הזמנות היום" value="12" delta="+3" deltaVariant="up" />
          <VendorKpiTile label="אחוז המרה" value="68%" delta="-4%" deltaVariant="warn" />
          <VendorKpiTile label="הכנסה היום" value="₪840" delta="-₪120" deltaVariant="down" />
          <VendorKpiTile label="דירוג" value="4.7" deltaVariant="neutral" />
        </Stack>
      </ComponentEntry>

      {/* VendorSidebar — P4 flush */}
      <ComponentEntry
        name="VendorSidebar"
        id="vendorsidebar-layout"
        path="src/components/ui/layout/VendorSidebar"
        description="Vendor dashboard sidebar nav with icon links and active state."
        tokens={['color-surface-overlay', 'color-text-primary', 'color-accent-vendor']}
        a11yNotes={['nav element with aria-label', 'active link aria-current="page"']}
        importSnippet={`import { VendorSidebar } from '@/components/ui/layout/VendorSidebar';`}
      >
        <div className="border-border-subtle relative h-64 w-16 overflow-hidden rounded border">
          <VendorSidebar currentPath="/vendor/dashboard" demo />
        </div>
      </ComponentEntry>

      {/* VendorBottomNav — P4 flush */}
      <ComponentEntry
        name="VendorBottomNav"
        id="vendorbottomnav-layout"
        path="src/components/ui/layout/VendorBottomNav"
        description="Mobile bottom navigation bar for the vendor dashboard."
        tokens={['color-surface-overlay', 'color-accent-vendor']}
        a11yNotes={['nav element with aria-label', 'active tab aria-current="page"']}
        importSnippet={`import { VendorBottomNav } from '@/components/ui/layout/VendorBottomNav';`}
      >
        <div className="border-border-subtle w-full overflow-hidden rounded border">
          <VendorBottomNav currentPath="/vendor/dashboard" demo />
        </div>
      </ComponentEntry>

      {/* VendorDealCard — P4 flush */}
      <ComponentEntry
        name="VendorDealCard"
        id="vendordealcard-domain"
        path="src/components/ui/domain/vendor/VendorDealCard"
        description="Deal card variant for the vendor dashboard — shows status, progress, and actions."
        tokens={['color-surface-raised', 'color-border-subtle', 'color-text-primary']}
        a11yNotes={['article with aria-label', 'status badge role="status"']}
        importSnippet={`import { VendorDealCard } from '@/components/ui/domain/vendor/VendorDealCard';`}
      >
        <Stack gap="3">
          <VendorDealCard
            variant="active"
            title="ארוחת בוקר זוגית"
            participants={18}
            targetParticipants={30}
            timeRemaining="08:00"
            price={60}
            imageSrc="https://imagedelivery.net/demo/breakfast-deal/card"
            imageAlt="ארוחת בוקר זוגית"
          />
          <VendorDealCard
            variant="pending"
            title="קינוח שוקולד"
            participants={0}
            targetParticipants={20}
            price={45}
            submittedAt="2026-05-14T08:30:00.000Z"
          />
          <VendorDealCard
            variant="closed"
            title="ארוחת ערב משפחתית"
            participants={25}
            targetParticipants={25}
            outcome="won"
            price={90}
          />
        </Stack>
      </ComponentEntry>

      {/* VendorActivityFeed — P4 flush */}
      <ComponentEntry
        name="VendorActivityFeed"
        id="vendoractivityfeed-domain"
        path="src/components/ui/domain/vendor/VendorActivityFeed"
        description="Chronological activity feed for vendor dashboard with tone-tinted dots."
        tokens={['color-surface-raised', 'color-text-muted', 'color-info-500', 'color-warning-500']}
        a11yNotes={['list of article elements', 'tone dot aria-hidden']}
        importSnippet={`import { VendorActivityFeed } from '@/components/ui/domain/vendor/VendorActivityFeed';`}
      >
        <VendorActivityFeed
          items={[
            { tone: 'info', title: 'הזמנה חדשה מאיתי כהן', time: 'לפני 30 דק׳' },
            { tone: 'warn', title: 'מלאי נמוך — נשארו 3', time: 'לפני שעה' },
            { tone: 'info', title: 'ביקורת חדשה ★★★★★', time: 'אתמול' },
          ]}
        />
      </ComponentEntry>

      {/* VendorFunnelChart — P4 flush */}
      <ComponentEntry
        name="VendorFunnelChart"
        id="vendorfunnelchart-domain"
        path="src/components/ui/domain/vendor/VendorFunnelChart"
        description="Conversion funnel bar chart for vendor dashboard analytics."
        tokens={['color-surface-raised', 'color-accent-vendor', 'color-text-muted']}
        a11yNotes={['figure with aria-label', 'bars aria-hidden with text values alongside']}
        importSnippet={`import { VendorFunnelChart } from '@/components/ui/domain/vendor/VendorFunnelChart';`}
      >
        <VendorFunnelChart
          steps={[
            { key: 'total_deals', value: 320 },
            { key: 'active_deals', value: 95 },
            { key: 'units_sold', value: 42 },
            { key: 'orders', value: 28 },
          ]}
        />
      </ComponentEntry>

      {/* PhonePreview — P4 flush */}
      <ComponentEntry
        name="PhonePreview"
        id="phonepreview-domain"
        path="src/components/ui/domain/vendor/PhonePreview"
        description="Phone frame preview showing how a deal looks to customers — used in deal creation flow."
        tokens={['color-surface-base', 'color-border-subtle', 'color-text-secondary']}
        a11yNotes={['aria-label on frame', 'empty state text visible']}
        importSnippet={`import { PhonePreview } from '@/components/ui/domain/vendor/PhonePreview';`}
      >
        <Row gap="6" wrap>
          <PhonePreview deal={{ title: 'ארוחת בוקר זוגית', priceNow: 60, priceWas: 120 }} />
          <PhonePreview />
        </Row>
      </ComponentEntry>

      {/* TierEditor — P4 flush */}
      <ComponentEntry
        name="TierEditor"
        id="tiereditor-domain"
        path="src/components/ui/domain/vendor/TierEditor"
        description="Drag-to-reorder price tiers editor for group deals."
        tokens={['color-surface-raised', 'color-border-subtle', 'color-text-primary']}
        a11yNotes={['each tier row has drag aria-label', 'remove button aria-label']}
        importSnippet={`import { TierEditor } from '@/components/ui/domain/vendor/TierEditor';`}
      >
        <TierEditor
          tiers={[
            { participants: 5, price: 80 },
            { participants: 10, price: 65 },
            { participants: 20, price: 50 },
          ]}
          priceFloor={40}
          onChange={() => {}}
        />
      </ComponentEntry>

      {/* AiRecommendationCard — P4 flush */}
      <ComponentEntry
        name="AiRecommendationCard"
        id="airecommendationcard-domain"
        path="src/components/ui/domain/vendor/AiRecommendationCard"
        description="AI suggestion card shown in vendor dashboard with accept/dismiss actions."
        tokens={['color-surface-raised', 'color-accent-ai', 'color-text-primary']}
        a11yNotes={['article with aria-label', 'accept/dismiss buttons aria-label from i18n']}
        importSnippet={`import { AiRecommendationCard } from '@/components/ui/domain/vendor/AiRecommendationCard';`}
      >
        <AiRecommendationCard
          body="הוסף תמונה לדיל — עסקאות עם תמונות מקבלות 3× יותר לחיצות"
          onAccept={() => {}}
          onDismiss={() => {}}
        />
      </ComponentEntry>

      {/* HoursRow — P4 flush */}
      <ComponentEntry
        name="HoursRow"
        id="hoursrow-domain"
        path="src/components/ui/domain/vendor/HoursRow"
        description="Single day row in the vendor hours editor — open/closed toggle with from/until time inputs."
        tokens={['color-surface-raised', 'color-border-subtle', 'color-text-primary']}
        a11yNotes={['Switch for open/closed with aria-label', 'time inputs labelled']}
        importSnippet={`import { HoursRow } from '@/components/ui/domain/vendor/HoursRow';`}
      >
        <Stack gap="2">
          <HoursRow
            day="sun"
            isOpen={true}
            openFrom="09:00"
            openUntil="17:00"
            onToggleOpen={() => {}}
            onChangeFrom={() => {}}
            onChangeUntil={() => {}}
          />
          <HoursRow
            day="sat"
            isOpen={false}
            openFrom="10:00"
            openUntil="14:00"
            onToggleOpen={() => {}}
            onChangeFrom={() => {}}
            onChangeUntil={() => {}}
          />
        </Stack>
      </ComponentEntry>

      {/* LanguageRegistryTable — Plan 1 Task 17 */}
      <ComponentEntry
        name="LanguageRegistryTable"
        id="languageregistrytable-domain"
        path="src/components/ui/admin/LanguageRegistryTable"
        description="Admin table for the languages registry. Shows code, native name, English name, direction, search config (postgres regconfig), active toggle, default badge, and edit action."
        tokens={[
          'color-surface-raised',
          'color-border-subtle',
          'color-text-primary',
          'color-text-muted',
          'brand-primary-100',
          'brand-primary-700',
        ]}
        a11yNotes={[
          'section aria-labelledby heading',
          'Switch has aria-label with language name',
          'Edit button aria-label includes language name',
          'Default badge visually distinct',
          'keyboard-navigable table',
        ]}
        importSnippet={`import { LanguageRegistryTable } from '@/components/ui/admin/LanguageRegistryTable';`}
      >
        <Stack gap="6">
          {/* Populated state */}
          <LanguageRegistryTable
            languages={[
              {
                code: 'he',
                nameNative: 'עברית',
                nameEnglish: 'Hebrew',
                direction: 'rtl',
                searchConfig: 'simple',
                isActive: true,
                isDefault: true,
                sortOrder: 0,
              },
              {
                code: 'en',
                nameNative: 'English',
                nameEnglish: 'English',
                direction: 'ltr',
                searchConfig: 'english',
                isActive: true,
                isDefault: false,
                sortOrder: 1,
              },
              {
                code: 'ar',
                nameNative: 'العربية',
                nameEnglish: 'Arabic',
                direction: 'rtl',
                searchConfig: 'arabic',
                isActive: false,
                isDefault: false,
                sortOrder: 2,
              },
            ]}
            onEdit={() => {}}
            onToggleActive={() => {}}
            onAdd={() => {}}
          />
          {/* Loading state */}
          <LanguageRegistryTable
            languages={[]}
            loading={true}
            onEdit={() => {}}
            onToggleActive={() => {}}
            onAdd={() => {}}
          />
          {/* Empty state */}
          <LanguageRegistryTable
            languages={[]}
            onEdit={() => {}}
            onToggleActive={() => {}}
            onAdd={() => {}}
          />
        </Stack>
      </ComponentEntry>

      {/* ── DealDurationPicker ───────────────────────────────────────── */}
      <section aria-labelledby="duration-picker-heading" className="flex flex-col gap-4">
        <h2 id="duration-picker-heading" className="text-text-primary text-base font-semibold">
          DealDurationPicker
        </h2>
        <p className="text-text-secondary text-sm">
          Smart preset row for deal window. businessHours=null hides &quot;עד סוף השבוע&quot;.
        </p>
        <DealDurationPickerDemo />
      </section>

      {/* ── PickupAddressSelector ────────────────────────────────────── */}
      <section aria-labelledby="pickup-addr-heading" className="flex flex-col gap-4">
        <h2 id="pickup-addr-heading" className="text-text-primary text-base font-semibold">
          PickupAddressSelector
        </h2>
        <p className="text-text-secondary text-sm">
          Fetches vendor address book, Drawer for pick/add. Saves new addresses as isPublic=false.
        </p>
        <PickupAddressSelector value="" onChange={() => {}} demo />
      </section>

      {/* ── PickupHoursField ─────────────────────────────────────────── */}
      <section aria-labelledby="pickup-hours-heading" className="flex flex-col gap-4">
        <h2 id="pickup-hours-heading" className="text-text-primary text-base font-semibold">
          PickupHoursField
        </h2>
        <p className="text-text-secondary text-sm">
          Two `TimeSpinner`s for pickup window — bound to separate `pickupStart` / `pickupEnd` form
          fields (HH:MM each). Used in deal form for pickup-method deals.
        </p>
        <PickupHoursField
          startValue="09:00"
          endValue="17:00"
          onStartChange={() => {}}
          onEndChange={() => {}}
        />
        <PickupHoursField
          startValue=""
          endValue=""
          onStartChange={() => {}}
          onEndChange={() => {}}
        />
      </section>

      {/* ── TagPills ─────────────────────────────────────────────────── */}
      <section
        id="tagpills-domain"
        aria-labelledby="tagpills-heading"
        className="flex flex-col gap-4"
      >
        <h2 id="tagpills-heading" className="text-text-primary text-base font-semibold">
          TagPills
        </h2>
        <p className="text-text-secondary text-sm">
          Wrapping list of clickable tag pills linking to <code>/tag/[slug]</code>. RTL-first,
          keyboard-accessible <code>&lt;a&gt;</code> elements inside{' '}
          <code>&lt;ul&gt;/&lt;li&gt;</code>. Renders nothing for empty arrays. Tokens:{' '}
          <code>bg-brand-primary-50</code>, <code>text-brand-primary-700</code>,
          <code>hover:bg-brand-primary-100</code>.
        </p>
        <div className="flex flex-col gap-3">
          <p className="text-text-secondary text-xs font-medium tracking-wide uppercase">
            locale=he (default — no count)
          </p>
          <TagPills
            locale="he"
            tags={[
              { id: '1', slug: 'food', nameHe: 'אוכל', nameEn: 'Food', usageCount: 34 },
              { id: '2', slug: 'drinks', nameHe: 'שתייה', nameEn: 'Drinks', usageCount: 12 },
              { id: '3', slug: 'bakery', nameHe: 'מאפייה', nameEn: 'Bakery', usageCount: 7 },
            ]}
          />
          <p className="text-text-secondary text-xs font-medium tracking-wide uppercase">
            locale=en + showCount
          </p>
          <TagPills
            locale="en"
            showCount
            tags={[
              { id: '1', slug: 'food', nameHe: 'אוכל', nameEn: 'Food', usageCount: 34 },
              { id: '2', slug: 'drinks', nameHe: 'שתייה', nameEn: 'Drinks', usageCount: 12 },
              { id: '3', slug: 'bakery', nameHe: 'מאפייה', nameEn: 'Bakery', usageCount: 7 },
            ]}
          />
          <p className="text-text-secondary text-xs font-medium tracking-wide uppercase">
            empty array → renders nothing
          </p>
          <span className="text-text-secondary text-xs italic">(no output)</span>
          <TagPills locale="he" tags={[]} />
        </div>
      </section>

      {/* ── CategoryPillFilter ───────────────────────────────────────────── */}
      <ComponentEntry
        name="CategoryPillFilter"
        id="categorypillfilter-domain"
        path="src/components/ui/domain/CategoryPillFilter/CategoryPillFilter.tsx"
        description="URL-syncing multi-select category pill row for /deals/all. Self-fetches categories from /api/deals/categories on mount. Toggling a chip writes ?cat=<ids> to the URL and navigates. Clears ?page= on every change."
        tokens={[
          'color-brand-primary-500',
          'color-brand-primary-50',
          'color-brand-primary-700',
          'color-surface-base',
          'color-border-default',
          'radius-full',
        ]}
        a11yNotes={[
          'role="region" aria-label wraps the chip row',
          'Each FilterChip is a <button> with aria-pressed',
          'data-testid="category-pill-filter" for E2E targeting',
          'Returns null until categories load — no layout shift slot',
          'Keyboard: Tab between chips, Space/Enter toggles',
          'Focus ring visible on all chips (focus-visible:outline)',
        ]}
        importSnippet={`import { CategoryPillFilter } from '@/components/ui/domain/CategoryPillFilter/CategoryPillFilter';

<CategoryPillFilter
  client:load
  initialSelectedIds={catIds}
  locale={locale}
  label="סנן לפי קטגוריה"
/>`}
      >
        {/* Static JSX mirror — component self-fetches and returns null in gallery (no API) */}
        <div
          role="region"
          aria-label="סנן לפי קטגוריה (preview)"
          className="flex flex-wrap items-center gap-2"
        >
          {['מזון', 'יופי', 'בריאות', 'אופנה', 'פנאי'].map((cat, i) => (
            <FilterChip key={cat} pressed={i === 1} onClick={() => {}}>
              {cat}
            </FilterChip>
          ))}
        </div>
      </ComponentEntry>

      {/* ── TagPillFilterUrl ─────────────────────────────────────────────── */}
      <ComponentEntry
        name="TagPillFilterUrl"
        id="tagpillfilterurl-domain"
        path="src/components/ui/domain/TagPillFilterUrl/TagPillFilterUrl.tsx"
        description="Thin URL-syncing wrapper over the callback-based search TagPillFilter. On tag toggle writes ?tagIds=<ids> to the URL and navigates; clears ?page=. Used in DealsFilterBar for /deals/all. TagPillFilter self-fetches from /api/deals/tags."
        tokens={[
          'color-brand-primary-500',
          'color-brand-primary-50',
          'color-brand-primary-700',
          'color-border-default',
          'radius-full',
        ]}
        a11yNotes={[
          'Inherits all a11y from TagPillFilter: FilterChip buttons with aria-pressed',
          'Dialog for "show all" tags: Radix Dialog, focus-trapped, Escape closes',
          'Dialog search input has aria-label',
          'Returns null until tags load from /api/deals/tags',
          'Tag list in dialog: scrollable <ul> with overflow-y-auto',
        ]}
        importSnippet={`import { TagPillFilterUrl } from '@/components/ui/domain/TagPillFilterUrl/TagPillFilterUrl';

<TagPillFilterUrl
  client:load
  initialSelectedTagIds={tagIds}
  locale={locale}
/>`}
      >
        {/* Static JSX mirror — component self-fetches from /api/deals/tags and returns null in gallery */}
        <div data-testid="tag-pill-filter" className="flex flex-wrap items-center gap-2">
          {['מבצע', 'חדש', 'קופון', 'אקסקלוסיבי', 'מוגבל'].map((tag, i) => (
            <FilterChip key={tag} pressed={i === 0} onClick={() => {}}>
              {tag}
            </FilterChip>
          ))}
        </div>
      </ComponentEntry>

      {/* ── SortDropdown ─────────────────────────────────────────────────── */}
      <ComponentEntry
        name="SortDropdown"
        id="sortdropdown-domain"
        path="src/components/ui/domain/SortDropdown/SortDropdown.tsx"
        description="Controlled Radix Select sort selector for /deals/all. Parent owns value and applies URL/state updates via onChange; the component itself has no URL, history, or navigation side effects."
        tokens={[
          'color-surface-base',
          'color-border-default',
          'color-text-primary',
          'color-brand-primary-500',
          'radius-md',
          'font-size-sm',
        ]}
        a11yNotes={[
          'SelectTrigger: aria-label from `label` prop — accessible without visible label',
          'Radix Select: keyboard-navigable listbox (Up/Down, Enter, Escape)',
          'SelectContent has role="listbox"; each SelectItem has role="option"',
          'aria-selected on active item (Radix built-in)',
          'Focus ring visible on trigger (focus-visible ring from Select primitive)',
        ]}
        importSnippet={`import { SortDropdown } from '@/components/ui/domain/SortDropdown/SortDropdown';

<SortDropdown
  client:load
  value={sort}
  onChange={setSort}
  label="מיין לפי"
  options={[
    { key: 'hot', label: 'הכי חמים' },
    { key: 'newest', label: 'חדשים ביותר' },
    { key: 'biggest-discount', label: 'הנחה גבוהה' },
    { key: 'ending-soon', label: 'נגמר בקרוב' },
  ]}
/>`}
      >
        <SortDropdownDemo />
      </ComponentEntry>

      {/* ── ClearFiltersLink ─────────────────────────────────────────────── */}
      <ComponentEntry
        name="ClearFiltersLink"
        id="clearfilterslink-domain"
        path="src/components/ui/domain/ClearFiltersLink/ClearFiltersLink.astro"
        description="Astro SSR-only anchor shown only when anyFilterActive filters are active. Renders a styled underline link to the base /deals/all URL with no filter params. Renders nothing (no DOM) when visible=false."
        tokens={[
          'color-brand-primary-700',
          'color-brand-primary-900',
          'color-brand-primary-500',
          'font-size-sm',
        ]}
        a11yNotes={[
          'Astro SSR — no client JS, no hydration',
          'Conditional render: outputs nothing when visible=false (no hidden wrapper)',
          'Plain <a> element — keyboard focusable, Enters activates',
          'focus-visible:outline-brand-primary-500 ring always visible',
          'href should point to the clean base URL with all filter params stripped',
        ]}
        importSnippet={`// Astro SSR component — import in .astro files only
---
import ClearFiltersLink from '@/components/ui/domain/ClearFiltersLink/ClearFiltersLink.astro';
---
<ClearFiltersLink
  href="/deals"
  label="נקה סינון"
  visible={anyFilterActive}
/>`}
      >
        {/* Static JSX mirror — Astro SSR component cannot be imported in React */}
        <div className="flex items-center gap-3">
          <span className="text-text-secondary text-sm">visible=true →</span>
          <a
            href="#clearfilterslink-domain"
            className="text-brand-primary-700 hover:text-brand-primary-900 focus-visible:outline-brand-primary-500 text-sm font-medium underline focus-visible:outline-2"
            onClick={(e) => e.preventDefault()}
          >
            נקה סינון
          </a>
          <span className="text-text-muted text-sm">| visible=false → (nothing rendered)</span>
        </div>
      </ComponentEntry>

      {/* ── DealCard.astro (SSR sibling) ─────────────────────────────────── */}
      <ComponentEntry
        name="DealCard (SSR variant)"
        id="dealcard-ssr-domain"
        path="src/components/ui/domain/DealCard/DealCard.astro"
        description="SSR-only Astro variant of DealCard for /deals/all grid. Zero client JS — no hydration. Uses the shared <Image> component for the AVIF → WebP → JPEG responsive pipeline. imageUrl must be a raw R2 key or absolute URL. See DealCard.tsx for the React interactive variant already registered above."
        tokens={[
          'color-surface-base',
          'color-border-default',
          'color-border-subtle',
          'color-brand-primary-50',
          'color-brand-primary-500',
          'color-brand-primary-700',
          'color-text-on-brand',
          'color-text-secondary',
          'color-text-muted',
          'radius-xl',
          'shadow-sm',
        ]}
        a11yNotes={[
          'Astro SSR — no client JS, zero hydration cost',
          'Outer <article> with data-deal-id for E2E targeting',
          '<a aria-label={title} wraps image + title — full card is one link',
          'focus-visible:outline-brand-primary-500 on card link',
          '<Image> alt defaults to title when imageAlt not provided',
          'Discount badge aria-hidden — decorative, price change communicated via text',
        ]}
        importSnippet={`// Astro SSR component — import in .astro files only
---
import DealCard from '@/components/ui/domain/DealCard/DealCard.astro';
---
<DealCard
  id={deal.id}
  title={deal.title}
  originalPrice={deal.originalPrice}
  discountedPrice={deal.discountedPrice}
  discountPercent={deal.discountPercent}
  imageUrl={deal.imageUrl}
  imageAlt={deal.title}
  hrefBase="/deal"
/>`}
      >
        {/* Static JSX mirror of DealCard.astro rendered output */}
        <article
          data-testid="deal-card"
          className="bg-surface-base border-border-default relative flex w-48 flex-col overflow-hidden rounded-xl border shadow-sm"
        >
          <div className="focus-visible:outline-brand-primary-500 block focus-visible:outline-2">
            <div className="bg-surface-inset relative aspect-square w-full overflow-hidden">
              <span
                className="text-text-muted absolute inset-0 flex items-center justify-center text-2xl"
                aria-hidden="true"
              >
                🖼
              </span>
              <span className="bg-brand-primary-500 text-text-on-brand absolute end-2 top-2 rounded-full px-2 py-0.5 text-xs font-bold">
                -50%
              </span>
            </div>
            <div className="p-2">
              <p className="text-text-secondary truncate text-xs font-medium">עוגת שוקולד בלגי</p>
            </div>
          </div>
          <div className="bg-brand-primary-50 border-border-subtle mt-auto border-t px-2 py-1.5">
            <div className="flex items-center gap-2">
              <span className="text-text-muted text-xs line-through">₪120</span>
              <span className="text-brand-primary-700 text-sm font-bold">₪60</span>
            </div>
          </div>
        </article>
      </ComponentEntry>

      {/* ── DealsBrowser composite ───────────────────────────────────────── */}
      <ComponentEntry
        name="DealsBrowser composite"
        id="dealsbrowser-domain"
        path="src/components/ui/domain/DealsBrowser/DealsBrowser.astro"
        description="SSR orchestrator for /deals/all. Composes DealsFilterBar.astro (type tabs + category/tag/price/sort filter row + ClearFiltersLink), DealsGrid.astro (responsive card grid with empty state), and PaginationLinks. All three sub-components live in the same DealsBrowser/ folder."
        tokens={[
          'color-surface-base',
          'color-brand-primary-500',
          'color-text-secondary',
          'radius-xl',
          'space-3',
        ]}
        a11yNotes={[
          'Astro SSR — zero client JS at the orchestrator level; filter islands hydrate independently',
          'DealsGrid renders <ul aria-label> with <li> per card',
          '<main id="main"> wraps grid for SkipLink target',
          'Empty state is a styled <p> — visible and screen-reader readable',
          'PaginationLinks <nav aria-label> only rendered when totalPages > 1',
          'DealsFilterBar <header> with filter controls labelled via aria-label',
        ]}
        importSnippet={`// Astro SSR orchestrator — use in .astro pages only
---
import DealsBrowser from '@/components/ui/domain/DealsBrowser/DealsBrowser.astro';
---
<DealsBrowser
  locale={locale}
  dict={t}
  heading="כל הדילים"
  type={type}
  categoryIds={catIds}
  tagIds={tagIds}
  priceMin={priceMin}
  priceMax={priceMax}
  sort={sort}
  page={page}
  totalPages={totalPages}
  buildHref={(p) => \`/deals?page=\${p}\`}
  deals={deals}
  anyFilterActive={anyFilterActive}
/>`}
      >
        {/* Static layout diagram — Astro composite cannot render in React */}
        <div className="border-border-default flex flex-col gap-0.5 overflow-hidden rounded-xl border text-xs">
          <div className="bg-surface-inset text-text-secondary px-4 py-2 font-mono">
            DealsFilterBar.astro
          </div>
          <div className="bg-surface-base text-text-muted px-4 py-1 ps-8">
            ↳ DealTypeTabs (type filter)
          </div>
          <div className="bg-surface-base text-text-muted px-4 py-1 ps-8">
            ↳ CategoryPillFilter (client:load)
          </div>
          <div className="bg-surface-base text-text-muted px-4 py-1 ps-8">
            ↳ TagPillFilterUrl (client:load)
          </div>
          <div className="bg-surface-base text-text-muted px-4 py-1 ps-8">
            ↳ PriceRangeFilter + SortDropdown (client:load)
          </div>
          <div className="bg-surface-base text-text-muted px-4 py-1 ps-8">
            ↳ ClearFiltersLink (SSR anchor)
          </div>
          <div className="bg-surface-inset text-text-secondary px-4 py-2 font-mono">
            DealsGrid.astro
          </div>
          <div className="bg-surface-base text-text-muted px-4 py-1 ps-8">
            ↳ DealCard.astro × N (SSR grid)
          </div>
          <div className="bg-surface-inset text-text-secondary px-4 py-2 font-mono">
            PaginationLinks.astro (when totalPages &gt; 1)
          </div>
        </div>
      </ComponentEntry>

      {/* ── DealsBrowserIsland ──────────────────────────────────────────── */}
      <ComponentEntry
        name="DealsBrowserIsland"
        id="dealsbrowserisland-domain"
        path="src/components/ui/domain/DealsBrowser/DealsBrowserIsland.tsx"
        description="Phase 3 React island for /deals/* pages. Receives SSR top-6 as initialData via TanStack Query (no first-render fetch). Refetches on filter/sort/page change via URL pushState + multideal:filters-changed event. Shows DealCardSkeleton for slots 7-12 while loading. Wraps in HydratedIsland (QueryClientProvider + LocaleProvider + ErrorBoundary)."
        tokens={[
          'color-surface-base',
          'color-surface-inset',
          'color-brand-primary-500',
          'color-border-default',
          'color-text-muted',
          'radius-xl',
          'space-3',
        ]}
        a11yNotes={[
          'Grid region has aria-live="polite" aria-busy={isFetching}',
          'Empty/error state uses role="alert" or plain <p> depending on origin',
          'Pagination <nav aria-label> with aria-current="page" on active page button',
          'All strings via deals_browse i18n namespace — Hebrew + English',
          'InlineDealCard is an <a> with full title as link text',
          'Skeleton slots via DealCardSkeleton (role=status, sr-only loading text)',
        ]}
        importSnippet={`import { DealsBrowserIsland } from '@/components/ui/domain/DealsBrowser/DealsBrowserIsland';

<DealsBrowserIsland
  initialDeals={top6Deals}
  initialTotal={total}
  initialUrlState={{ type, catSlug, tagSlugs, page: 1, sort: 'hot', priceMin: 0, priceMax: 0 }}
  locale={locale}
  client:load
/>`}
      >
        <DealsBrowserIsland
          demo
          initialDeals={[
            {
              id: 'demo-1',
              title: 'עיסוי שוודי + ארומתרפי 60 דקות',
              originalPrice: 320,
              discountedPrice: 149,
              discountPercent: 53,
              imageUrl: null,
              vendorId: 'v1',
              vendorName: 'ספא רוגע',
              vendorAvatarUrl: null,
              city: '',
              dealType: 'COUPON' as const,
              windowEnd: null,
              stockRemaining: 10,
              stockTotal: 50,
              isHotDeal: true,
              soldCount: 40,
            },
            {
              id: 'demo-2',
              title: 'ארוחת שף זוגית במסעדת אלומות',
              originalPrice: 280,
              discountedPrice: 139,
              discountPercent: 50,
              imageUrl: null,
              vendorId: 'v2',
              vendorName: 'אלומות',
              vendorAvatarUrl: null,
              city: '',
              dealType: 'COUPON' as const,
              windowEnd: null,
              stockRemaining: 5,
              stockTotal: 30,
              isHotDeal: false,
              soldCount: 25,
            },
            {
              id: 'demo-3',
              title: 'קורס צילום מקצועי — 3 מפגשים',
              originalPrice: 600,
              discountedPrice: 249,
              discountPercent: 58,
              imageUrl: null,
              vendorId: 'v3',
              vendorName: 'סטודיו פוקוס',
              vendorAvatarUrl: null,
              city: '',
              dealType: 'GROUP' as const,
              windowEnd: null,
              stockRemaining: 8,
              stockTotal: 20,
              isHotDeal: false,
              soldCount: 12,
            },
          ]}
          initialTotal={3}
          initialUrlState={{
            type: undefined,
            catSlug: undefined,
            tagSlugs: [],
            page: 1,
            sort: 'hot',
            priceMin: 0,
            priceMax: 0,
          }}
          locale="he"
        />
      </ComponentEntry>

      {/* ── AiVerdictPanel ──────────────────────────────────────────────── */}
      <ComponentEntry
        name="AiVerdictPanel"
        id="ai-verdict-panel"
        path="src/components/ui/domain/admin/AiVerdictPanel/index.tsx"
        description="Displays the AI moderation verdict for a flagged image: decision pill, confidence score, model ID, check timestamp, human-readable reason, and collapsible raw JSON payload. Used inside the admin approval inbox modal."
        tokens={[
          'color-success-50',
          'color-success-700',
          'color-warning-50',
          'color-warning-700',
          'color-danger-50',
          'color-danger-700',
          'color-neutral-100',
          'color-neutral-600',
          'color-surface-inset',
          'color-border-default',
          'color-text-secondary',
          'color-text-primary',
          'color-text-muted',
        ]}
        a11yNotes={[
          '<section> with aria-label from i18n key ai_verdict_title',
          '<details>/<summary> for raw payload — natively keyboard-accessible, Enter/Space toggles',
          'Decision pill is a <span> (display-only, not interactive)',
          'Time formatted HH:mm per time-formatting law — never raw ISO string',
          'RTL: logical props only (ps, pe, ms, me)',
        ]}
        importSnippet={`import { AiVerdictPanel } from '@/components/ui/domain/admin/AiVerdictPanel';

<AiVerdictPanel
  decision="FLAG"
  score={0.87}
  model="gemini-3-flash-preview"
  checkedAt={new Date()}
  reason="Image contains potentially misleading pricing text"
  rawPayload={{ flaggedCategories: ['misleading'], confidence: 0.87 }}
/>`}
      >
        <div className="flex flex-col gap-4">
          <AiVerdictPanel
            decision="FLAG"
            score={0.87}
            model="gemini-3-flash-preview"
            checkedAt={new Date('2026-04-12T09:00:00Z')}
            reason="Image contains potentially misleading pricing text overlay."
            rawPayload={{ flaggedCategories: ['misleading'], confidence: 0.87, raw: 'truncated' }}
          />
          <AiVerdictPanel
            decision="ERROR"
            score={null}
            model="gemini-3-flash-preview"
            checkedAt={new Date('2026-04-12T09:05:00Z')}
            reason="Vision API returned an empty response. Image could not be processed."
            rawPayload={null}
          />
          <AiVerdictPanel
            decision="PASS"
            score={0.98}
            model="gemini-3-flash-preview"
            checkedAt={new Date('2026-04-12T09:10:00Z')}
            reason={null}
            rawPayload={{ confidence: 0.98 }}
          />
          <AiVerdictPanel
            decision="REJECT"
            score={0.95}
            model="gemini-3-flash-preview"
            checkedAt={new Date('2026-04-12T09:15:00Z')}
            reason="Explicit content detected. Image violates platform policy."
            rawPayload={{ flaggedCategories: ['explicit'], confidence: 0.95 }}
          />
        </div>
      </ComponentEntry>

      {/* ── CascadePreviewPanel ──────────────────────────────────────────── */}
      <ComponentEntry
        name="CascadePreviewPanel"
        id="cascade-preview-panel"
        path="src/components/ui/domain/admin/CascadePreviewPanel/index.tsx"
        description="Renders a pre-computed CascadePreview showing DB rows affected, the public fallback shown to users after rejection, and owner-notification status. Does NOT import CASCADE_REGISTRY — safe in React islands."
        tokens={[
          'color-warning-500',
          'color-success-500',
          'color-neutral-400',
          'color-success-700',
          'color-text-muted',
          'color-text-primary',
          'color-text-secondary',
          'color-surface-inset',
          'color-border-default',
        ]}
        a11yNotes={[
          '<section> with aria-label from i18n key cascade_preview_title',
          'Affected rows in <ul role="list"> with <li> per item',
          'Icons are aria-hidden — decorative only',
          'Owner-notified indicator uses CheckCircle vs Circle icon + color token',
          'RTL: logical props only',
        ]}
        importSnippet={`import { CascadePreviewPanel } from '@/components/ui/domain/admin/CascadePreviewPanel';

// preview is computed server-side via CASCADE_REGISTRY[purpose].preview(ctx)
<CascadePreviewPanel preview={item.cascadePreview} />`}
      >
        <div className="flex flex-col gap-4">
          <CascadePreviewPanel
            preview={{
              entityRowsAffected: [
                'vendors.logo_approval_status → REJECTED',
                'vendors.pending_logo_url → NULL',
              ],
              publicFallback: 'cascade_preview_vendor_logo_fallback',
              ownerNotified: true,
              emailSent: false,
            }}
          />
          <CascadePreviewPanel
            preview={{
              entityRowsAffected: ['vendor_gallery_images.approval_status → REJECTED'],
              publicFallback: 'cascade_preview_gallery_omit',
              ownerNotified: false,
              emailSent: false,
            }}
          />
          <CascadePreviewPanel
            preview={{
              entityRowsAffected: [],
              publicFallback: 'cascade_preview_none',
              ownerNotified: false,
              emailSent: false,
            }}
          />
        </div>
      </ComponentEntry>

      {/* VendorLogoFallback */}
      <ComponentEntry
        name="VendorLogoFallback"
        id="vendor-logo-fallback"
        path="src/components/ui/domain/VendorLogoFallback"
        description="Initial-letter circle shown when a vendor has no approved logo. Three size presets match the real logo footprint in BusinessProfile and profile pages."
        tokens={['color-brand-primary-100', 'color-brand-primary-700', 'radius-xl']}
        a11yNotes={[
          'role=img + aria-label from vendor_profile.vendor_logo_fallback_alt i18n key',
          'Direction-agnostic — circle layout identical in RTL and LTR',
        ]}
        importSnippet={`import { VendorLogoFallback } from '@/components/ui/domain/VendorLogoFallback';`}
      >
        <div className="flex items-end gap-4">
          <VendorLogoFallback name="פיצה רומא" size="sm" />
          <VendorLogoFallback name="פיצה רומא" size="md" />
          <VendorLogoFallback name="פיצה רומא" size="lg" />
          <VendorLogoFallback name="Amazing Bakery" size="md" />
        </div>
      </ComponentEntry>

      {/* RejectionBannerList */}
      <ComponentEntry
        name="RejectionBannerList"
        id="rejection-banner-list"
        path="src/components/ui/domain/RejectionBannerList"
        description="Renders a RejectionBanner (warning/amber) for each unresolved image.rejected notification. Used beside vendor dashboard and user profile pages — maps payload.purpose to localized title and payload.replaceHref to Replace CTA."
        tokens={['color-warning-50', 'color-warning-500', 'color-warning-700']}
        a11yNotes={[
          'Wrapping <div role=region>',
          'Each inner banner is <aside role=alert>',
          'All strings via vendor_dashboard i18n namespace',
        ]}
        importSnippet={`import { RejectionBannerList } from '@/components/ui/domain/RejectionBannerList';`}
      >
        <RejectionBannerList
          notifications={[
            {
              id: 'n1',
              payload: {
                purpose: 'vendor_logo',
                reason: 'הלוגו מכיל סימן מים',
                replaceHref: '/vendor/settings?tab=logo',
              },
            },
            {
              id: 'n2',
              payload: {
                purpose: 'vendor_hero',
                reason: 'תמונת הנושא מטושטשת',
                replaceHref: '/vendor/settings?tab=hero',
              },
            },
          ]}
        />
      </ComponentEntry>

      {/* BirthdayField */}
      <ComponentEntry
        name="BirthdayField"
        id="birthdayfield-domain"
        path="src/components/ui/domain/BirthdayField"
        description="Month + day pickers for onboarding birthday collection (no year — GDPR-light). Days clamp to valid range per month (Feb → 1–29, Apr/Jun/Sep/Nov → 1–30). Partial-hint surfaced on Save-click when only one of month/day is set."
        tokens={['color-text-muted', 'color-warning-50', 'color-warning-500']}
        a11yNotes={[
          'Visible birthday_label wraps the field group',
          'Each Select has sr-only Label associated via htmlFor/id',
          'InlineNotice tone=warning uses role=alert — announced by screen readers',
          'All strings via onboarding i18n namespace — zero raw literals',
          'RTL: logical gap-3, flex layout direction-agnostic',
        ]}
        importSnippet={`import { BirthdayField } from '@/components/ui/domain/BirthdayField';`}
      >
        <div className="flex flex-col gap-6">
          {/* empty state */}
          <BirthdayField month={null} day={null} onChange={() => {}} />

          {/* partial + hint */}
          <BirthdayField month={5} day={null} onChange={() => {}} showPartialHint />

          {/* both set */}
          <BirthdayField month={3} day={14} onChange={() => {}} />

          {/* Feb 29 clamped */}
          <BirthdayField month={2} day={29} onChange={() => {}} />
        </div>
      </ComponentEntry>

      {/* CitySelectField */}
      <ComponentEntry
        name="CitySelectField"
        id="cityselectfield-domain"
        path="src/components/ui/domain/CitySelectField"
        description="Israeli city autocomplete bound to /api/address/cities (data.gov.il). Inline Popover+Input combobox with type-to-filter. Autodetect button calls navigator.geolocation on click only (no cold prompt) → POSTs to /api/address/reverse-geocode → autofills. Failure shows InlineNotice tone=warning. Value shape: { city, cityCode }."
        tokens={[
          'color-text-muted',
          'color-brand-primary-50',
          'color-brand-primary-700',
          'color-surface-raised',
          'color-text-primary',
          'color-text-secondary',
        ]}
        a11yNotes={[
          'Label htmlFor binding to combobox input id',
          'Input has role=combobox + aria-expanded + aria-haspopup + aria-autocomplete + aria-controls + aria-activedescendant',
          'Option list is <ul role=listbox> with <li role=option aria-selected>',
          'Autodetect Button has aria-label via i18n city_autodetect key',
          'InlineNotice tone=warning renders role=alert — announced by screen readers',
          'Blur resets input to last confirmed value — prevents free-text submission',
          'All strings via onboarding i18n namespace — zero raw literals',
          'RTL: logical flex-wrap, gap-3 direction-agnostic',
        ]}
        importSnippet={`import { CitySelectField } from '@/components/ui/domain/CitySelectField';`}
      >
        <div className="flex flex-col gap-6">
          {/* empty state */}
          <div>
            <p className="text-text-muted mb-2 text-sm">empty state</p>
            <CitySelectField value={null} onChange={() => {}} id="city-ds-empty" />
          </div>

          {/* value selected */}
          <div>
            <p className="text-text-muted mb-2 text-sm">value selected</p>
            <CitySelectField
              value={{ city: 'תל אביב-יפו', cityCode: '3000' }}
              onChange={() => {}}
              id="city-ds-selected"
            />
          </div>

          {/* autodetect-error state */}
          <div>
            <p className="text-text-muted mb-2 text-sm">autodetect-error (simulated via wrapper)</p>
            <CitySelectFieldErrorDemo />
          </div>
        </div>
      </ComponentEntry>

      {/* ─── OnboardingSheet ─────────────────────────────────────────────── */}
      <ComponentEntry
        id="onboardingsheet-domain"
        name="OnboardingSheet"
        path="src/features/onboarding/OnboardingSheet"
        description="Full-screen (mobile) / centered (desktop) blocking onboarding modal. Esc and backdrop dismiss are disabled — only Save or Skip close it."
        tokens={[
          'color-surface-default',
          'color-text-primary',
          'color-text-secondary',
          'color-brand-primary-600',
          'space-6',
          'z-modal',
        ]}
        a11yNotes={[
          'DialogTitle wired to aria-labelledby by Radix — screen readers announce modal title on open',
          'Focus trap: Tab cycles within modal only (Radix Dialog handles)',
          'Esc key: preventDefault — user must Save or Skip',
          'Backdrop click: preventDefault — user must Save or Skip',
          'All strings via onboarding i18n namespace',
          'email_verified_chip Pill uses tone=success — not color-only signal (text label present)',
        ]}
        importSnippet={`import { OnboardingSheet } from '@/features/onboarding/OnboardingSheet';`}
      >
        <div className="flex flex-col gap-6">
          <OnboardingSheetDemo emailVerifiedFlag={false} label="open-blank (no verified chip)" />
          <OnboardingSheetDemo emailVerifiedFlag={true} label="open-with-verified-chip" />
        </div>
      </ComponentEntry>
    </Stack>
  );
}

/** Renders CitySelectField in error state for DS gallery. */
function CitySelectFieldErrorDemo() {
  const [val, setVal] = useState<CityValue | null>(null);
  return (
    <div className="flex flex-col gap-2">
      <CitySelectField value={val} onChange={setVal} id="city-ds-error" />
      <p className="text-text-muted text-xs">
        (Click &quot;Autodetect&quot; then deny geolocation to see the warning notice)
      </p>
    </div>
  );
}

// ─── OnboardingSheet DS demo ───────────────────────────────────────────────

/** Controlled open/close demo for OnboardingSheet (avoids window.__ONBOARDING__ dependency). */
function OnboardingSheetDemo({
  emailVerifiedFlag,
  label,
}: {
  emailVerifiedFlag: boolean;
  label: string;
}) {
  const [open, setOpen] = useState(false);
  return (
    <div className="flex flex-col gap-2">
      <p className="text-text-muted text-sm">{label}</p>
      <Button type="button" variant="secondary" size="sm" onClick={() => setOpen(true)}>
        Open
      </Button>
      <OnboardingSheet
        open={open}
        emailVerifiedFlag={emailVerifiedFlag}
        onComplete={() => setOpen(false)}
      />
    </div>
  );
}

// ─── Near You Section (Wave 3) ───────────────────────────────────────────────

function CitySelectorDemo() {
  const [city, setCity] = useState<string | null>(null);
  const cities = [
    { city: 'תל אביב', cityCode: 'tel-aviv', lat: 32.0853, lng: 34.7818, dealCount: 12 },
    { city: 'חיפה', cityCode: 'haifa', lat: 32.794, lng: 34.9896, dealCount: 7 },
  ];
  return (
    <Stack gap="4">
      <div>
        <p className="text-text-muted mb-2 text-xs">Default (full width)</p>
        <CitySelector value={city} onChange={setCity} cities={cities} />
      </div>
      <div>
        <p className="text-text-muted mb-2 text-xs">compact=true (mobile header)</p>
        <CitySelector value={city} onChange={setCity} compact cities={cities} />
      </div>
    </Stack>
  );
}

function UseMyLocationButtonDemo() {
  return (
    <Stack gap="4">
      <div>
        <p className="text-text-muted mb-2 text-xs">Default (with label)</p>
        <UseMyLocationButton onCoords={() => null} />
      </div>
      <div>
        <p className="text-text-muted mb-2 text-xs">iconOnly=true (mobile)</p>
        <UseMyLocationButton onCoords={() => null} iconOnly />
      </div>
    </Stack>
  );
}

function RadiusSliderDemo() {
  const [radius, setRadius] = useState(10);
  return (
    <Stack gap="4">
      <div className="max-w-xs">
        <p className="text-text-muted mb-2 text-xs">Enabled (geolocation granted)</p>
        <RadiusSlider value={radius} onChange={setRadius} />
      </div>
      <div className="max-w-xs">
        <p className="text-text-muted mb-2 text-xs">disabled=true (awaiting geolocation)</p>
        <RadiusSlider value={radius} onChange={setRadius} disabled />
      </div>
    </Stack>
  );
}

function OpeningHoursFilterDemo() {
  const [hours, setHours] = useState<OpeningHoursValue>({ mode: 'any' });
  return (
    <Stack gap="4">
      <div className="max-w-sm">
        <p className="text-text-muted mb-2 text-xs">mode=any (default)</p>
        <OpeningHoursFilter value={hours} onChange={setHours} />
      </div>
      <div className="max-w-sm">
        <p className="text-text-muted mb-2 text-xs">mode=openNow (click tab to set)</p>
        <OpeningHoursFilter value={{ mode: 'openNow' }} onChange={setHours} />
      </div>
      <div className="max-w-sm">
        <p className="text-text-muted mb-2 text-xs">mode=window (day chips + time inputs)</p>
        <OpeningHoursFilter
          value={{ mode: 'window', dayOfWeek: 0, startMin: 480, endMin: 1200 }}
          onChange={setHours}
        />
      </div>
    </Stack>
  );
}

function SortDropdownDemo() {
  const [sort, setSort] = useState<BrowseSort>('hot');
  return (
    <SortDropdown
      value={sort}
      onChange={setSort}
      label="מיין לפי"
      options={[
        { key: 'hot', label: 'הכי חמים' },
        { key: 'newest', label: 'חדשים ביותר' },
        { key: 'biggest-discount', label: 'הנחה גבוהה' },
        { key: 'ending-soon', label: 'נגמר בקרוב' },
      ]}
    />
  );
}

function ViewToggleDemo() {
  const [view, setView] = useState<ViewMode>('gallery');
  return (
    <Stack gap="2">
      <p className="text-text-muted text-xs">Current: {view}</p>
      <ViewToggle value={view} onChange={setView} />
    </Stack>
  );
}

function FacetsDemo() {
  const [catIds, setCatIds] = useState<string[]>([]);
  const [tagKeys, setTagKeys] = useState<string[]>([]);
  const [dealTypes, setDealTypes] = useState<FacetDealType[]>([]);
  const [tiers, setTiers] = useState<DiscountTier[]>([]);
  const [price, setPrice] = useState<[number, number]>([0, 2000]);

  const mockCategories = [
    { id: '1', slug: 'food', nameHe: 'מזון', nameEn: 'Food', dealCount: 42 },
    { id: '2', slug: 'beauty', nameHe: 'יופי', nameEn: 'Beauty', dealCount: 18 },
    { id: '3', slug: 'fashion', nameHe: 'אופנה', nameEn: 'Fashion', dealCount: 7 },
  ];
  const mockTags = [
    { key: 'vegetarian', name: 'צמחוני' },
    { key: 'delivery', name: 'משלוח' },
    { key: 'weekend', name: 'סוף שבוע' },
  ];

  return (
    <div className="flex flex-wrap gap-6">
      <div className="w-56">
        <CategoryFacet
          label="קטגוריה"
          categories={mockCategories}
          selectedIds={catIds}
          onToggle={(id) =>
            setCatIds((p) => (p.includes(id) ? p.filter((x) => x !== id) : [...p, id]))
          }
          locale="he"
          showCounts={true}
        />
      </div>
      <div className="w-56">
        <TagFacet
          label="תגיות"
          tags={mockTags}
          selected={tagKeys}
          onToggle={(k) =>
            setTagKeys((p) => (p.includes(k) ? p.filter((x) => x !== k) : [...p, k]))
          }
        />
      </div>
      <div className="w-56">
        <DealTypeFacet label="סוג" selected={dealTypes} onChange={setDealTypes} />
      </div>
      <div className="w-56">
        <DiscountTierFacet
          label="הנחה"
          tierLabels={{ 50: '50%+', 60: '60%+', 70: '70%+' }}
          selected={tiers}
          onChange={setTiers}
        />
      </div>
      <div className="w-56">
        <PriceRangeFacet
          label="מחיר"
          floor={0}
          ceiling={2000}
          value={price}
          onChange={setPrice}
          currency="₪"
          histogram={[
            1, 3, 6, 10, 16, 20, 26, 22, 17, 12, 14, 9, 7, 5, 8, 4, 3, 2, 1, 2, 1, 1, 0, 1, 0, 1, 0,
            1,
          ]}
        />
      </div>
    </div>
  );
}

function NearYouSection() {
  return (
    <Stack gap="6">
      {/* CitySelector */}
      <ComponentEntry
        name="CitySelector"
        id="cityselector-domain"
        path="src/components/ui/domain/CitySelector"
        description="Radix Select skin that lazy-fetches /api/cities via TanStack Query. Compact variant for mobile headers shows MapPin + truncated label. Shows (count) per city."
        tokens={['color-brand-primary-600', 'color-neutral-200', 'color-text-muted']}
        a11yNotes={[
          'Radix Select: keyboard navigable, ARIA expanded/haspopup, focus managed',
          'SelectTrigger has aria-label from i18n near_you.citySelector_placeholder',
          'Typeahead built into Radix Select for fast city search',
          'DirectionProvider in LocaleProvider wires RTL correctly',
        ]}
        importSnippet={`import { CitySelector } from '@/components/ui/domain/CitySelector';`}
      >
        <CitySelectorDemo />
      </ComponentEntry>

      {/* UseMyLocationButton */}
      <ComponentEntry
        name="UseMyLocationButton"
        id="usemylocationbutton-domain"
        path="src/components/ui/domain/UseMyLocationButton"
        description="Triggers navigator.geolocation.getCurrentPosition (10s timeout, enableHighAccuracy:false). States: idle/requesting/granted/denied/unsupported. Coords go straight to onCoords — never stored. iconOnly for mobile headers."
        tokens={['color-brand-primary-100', 'color-brand-primary-700', 'color-surface-hover']}
        a11yNotes={[
          'Native <button> — keyboard accessible',
          'aria-label from near_you.useMyLocation i18n key',
          'aria-pressed=true when granted',
          'aria-busy=true while requesting',
          'disabled when unsupported or requesting — cursor-not-allowed',
          'SSR-safe: navigator.geolocation only accessed inside click handler',
        ]}
        importSnippet={`import { UseMyLocationButton } from '@/components/ui/domain/UseMyLocationButton';`}
      >
        <UseMyLocationButtonDemo />
      </ComponentEntry>

      {/* RadiusSlider */}
      <ComponentEntry
        name="RadiusSlider"
        id="radiusslider-domain"
        path="src/components/ui/domain/RadiusSlider"
        description="Single-handle 1–50 km radius slider built on Radix Slider. Disabled until geolocation coords granted. Large thumb (20×20px) for mobile touch. Numeric scale always LTR regardless of locale."
        tokens={['color-brand-primary-600', 'color-neutral-200', 'color-text-secondary']}
        a11yNotes={[
          'Radix Slider: role=slider, aria-valuenow, aria-valuemin/max, aria-orientation=horizontal',
          'aria-label from near_you.radius_label + value + km_unit',
          'aria-labelledby links to visible label span',
          'Keyboard: ArrowLeft/Right ±1 km, PageUp/Down ±10 km, Home/End',
          'Disabled state: opacity-40 + pointer-events-none, communicated via aria-disabled',
        ]}
        importSnippet={`import { RadiusSlider } from '@/components/ui/domain/RadiusSlider';`}
      >
        <RadiusSliderDemo />
      </ComponentEntry>

      {/* OpeningHoursFilter */}
      <ComponentEntry
        name="OpeningHoursFilter"
        id="openinghoursfilter-domain"
        path="src/components/ui/domain/OpeningHoursFilter"
        description="Three-mode Radix Tabs filter: any (no restriction) / openNow / window (custom day+time). Window panel: day-of-week FilterChip group + native <input type=time> start/end."
        tokens={['color-surface-raised', 'color-border-default', 'color-brand-primary-500']}
        a11yNotes={[
          'Radix Tabs: keyboard Left/Right arrows cycle tabs, Space/Enter activate',
          'TabList has aria-label from near_you.filter',
          'Day chips: role=group container + FilterChip aria-pressed per day',
          'Time inputs: each has associated <label htmlFor>',
          'Native <input type=time> provides OS-level time picker accessible to all ATs',
        ]}
        importSnippet={`import { OpeningHoursFilter } from '@/components/ui/domain/OpeningHoursFilter';`}
      >
        <OpeningHoursFilterDemo />
      </ComponentEntry>

      {/* ViewToggle */}
      <ComponentEntry
        name="ViewToggle"
        id="viewtoggle-domain"
        path="src/components/ui/domain/ViewToggle"
        description="Gallery / map segmented control using SegmentedControl primitive. Icons are aria-hidden; visible text provides accessible label. RTL-aware arrow key navigation."
        tokens={['color-surface-raised', 'color-brand-primary-600']}
        a11yNotes={[
          'Wraps SegmentedControl: role=radiogroup + role=radio/aria-checked per option',
          'Roving tabIndex: only selected option is in tab order',
          'ArrowLeft/Right cycle options; RTL-aware (reads computed dir)',
          'Icons are aria-hidden — text labels carry the name',
          'aria-label on container: near_you.gallery / near_you.map',
        ]}
        importSnippet={`import { ViewToggle } from '@/components/ui/domain/ViewToggle';`}
      >
        <ViewToggleDemo />
      </ComponentEntry>

      {/* Composable Facet Primitives */}
      <ComponentEntry
        name="Facets (CategoryFacet · TagFacet · DealTypeFacet · DiscountTierFacet · PriceRangeFacet)"
        id="facets-domain"
        path="src/components/ui/domain/facets"
        description="Composable filter primitives. Each is standalone — compose inside FilterShell for sidebar chrome. CategoryFacet supports showCounts prop for search page (with count badges) vs near-you (without). TagFacet is key-agnostic (pass slug or ID). DealType/DiscountTier use opt-in semantics (empty = no filter)."
        tokens={[
          'color-brand-primary-50',
          'color-brand-primary-300',
          'color-brand-primary-700',
          'color-surface-hover',
        ]}
        a11yNotes={[
          'role=group + aria-label on each facet group',
          'aria-pressed on toggle buttons for screen readers',
          'Focus rings via focus-visible:ring-brand-primary-500',
          'Slider uses native range input with ARIA labels',
        ]}
        importSnippet={`import { CategoryFacet, TagFacet, DealTypeFacet, DiscountTierFacet, PriceRangeFacet } from '@/components/ui/domain/facets';`}
      >
        <FacetsDemo />
      </ComponentEntry>

      {/* MapView stub */}
      <ComponentEntry
        name="MapView (W3 stub)"
        id="mapview-domain"
        path="src/components/ui/domain/MapView"
        description="Placeholder for Wave 6 Leaflet implementation. Renders a styled bg-surface-raised div with deal count. Full react-leaflet MapContainer with clustering, popups, and radius circle lands in W6."
        tokens={['color-surface-raised', 'color-text-muted']}
        a11yNotes={[
          'role=region with aria-label="Map view placeholder"',
          'W6 will add: keyboard pan/zoom (Leaflet default), sr-only summary p, noscript fallback',
        ]}
        importSnippet={`import { MapView } from '@/components/ui/domain/MapView';`}
      >
        <MapView deals={[]} center={{ lat: 32.08, lng: 34.78 }} radius={10} />
      </ComponentEntry>

      {/* ShareInvite */}
      <ComponentEntry
        name="ShareInvite"
        id="share-invite-domain"
        path="src/features/referrals/ShareInvite"
        description="Referral invite card. Fetches GET /api/referrals/me for the user's link; if no link exists, POST /api/referrals/link mints one. Uses navigator.share when available; clipboard copy fallback with toast confirmation. All copy from the referrals i18n namespace."
        tokens={['color-surface-raised', 'color-brand-primary-700', 'color-text-secondary']}
        a11yNotes={[
          '<section> with aria-labelledby pointing to the h2 heading',
          'Share button: aria-label from referrals.share_cta or referrals.create_link',
          'Clipboard toast: ToastTitle with referrals.link_copied',
          'Loading state: aria-busy=true on the container skeleton',
        ]}
        importSnippet={`import { ShareInvite } from '@/features/referrals/ShareInvite';`}
      >
        <ShareInviteDemo />
      </ComponentEntry>
    </Stack>
  );
}

// ---------------------------------------------------------------------------
// ShareInvite demo — shows the static layout (unauthenticated/no-link state)
// ---------------------------------------------------------------------------

function ShareInviteDemo() {
  return (
    <div className="max-w-sm">
      <div
        className="bg-surface-raised flex flex-col gap-3 rounded-xl p-4"
        aria-label="ShareInvite demo (static)"
      >
        <h3 className="text-text-primary text-base font-bold">הזמינו חברים, קבלו קרדיט</h3>
        <p className="text-text-secondary text-sm">
          כשחבר/ה שהזמנתם רוכשים עסקה ראשונה — אתם מקבלים ₪20 קרדיט, והם מקבלים ₪20 הנחה.
        </p>
        <p className="text-text-muted text-xs italic">
          (Demo — live variant fetches /api/referrals/me and mints link on demand)
        </p>
      </div>
    </div>
  );
}
