{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "hotel-booking-reservation",
  "title": "Hotel Booking Reservation",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/hotel-booking-reservation/hotel-booking-reservation.component.ts",
      "content": "// Edit this file to change the room, photos, upgrades and copy. State is local -- wire\n// `book()` to your own reservation API.\nimport { Component, Input, OnInit, signal, ChangeDetectionStrategy } from '@angular/core'\nimport {\n  Baby,\n  Bath,\n  BedDouble,\n  Building2,\n  Calendar,\n  CalendarDays,\n  Check,\n  CircleCheck,\n  Coffee,\n  CreditCard,\n  Heart,\n  Info,\n  Lock,\n  LucideAngularModule,\n  MapPin,\n  Maximize2,\n  Minus,\n  Plus,\n  RotateCcw,\n  Share2,\n  ShieldCheck,\n  Star,\n  Users,\n  Waves,\n  Wifi,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiCheckboxComponent } from '@/ui/checkbox/checkbox.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\nexport type HotelBookingUpgrade = 'balcony' | 'breakfast' | 'chauffeur' | 'spa'\n\nexport interface HotelBookingPhoto {\n  url: string\n  title: string\n}\n\nconst roomPhotos: HotelBookingPhoto[] = [\n  {\n    url: 'https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?w=1200&auto=format&fit=crop&q=80',\n    title: 'King Master Suite & Ocean Balcony',\n  },\n  {\n    url: 'https://images.unsplash.com/photo-1571896349842-33c89424de2d?w=800&auto=format&fit=crop&q=80',\n    title: 'Atlantic Ocean Sunrise Balcony',\n  },\n  {\n    url: 'https://images.unsplash.com/photo-1584622650111-993a426fbf0a?w=800&auto=format&fit=crop&q=80',\n    title: 'Italian Marble Spa Bathroom',\n  },\n  {\n    url: 'https://images.unsplash.com/photo-1540541338287-41700207dee6?w=800&auto=format&fit=crop&q=80',\n    title: 'Private Beach Club & Ocean Cabanas',\n  },\n]\n\nconst currency = new Intl.NumberFormat('en-US', {\n  style: 'currency',\n  currency: 'USD',\n  minimumFractionDigits: 2,\n  maximumFractionDigits: 2,\n})\n\nfunction toIsoDate(date: Date): string {\n  const y = date.getFullYear()\n  const m = String(date.getMonth() + 1).padStart(2, '0')\n  const d = String(date.getDate()).padStart(2, '0')\n  return `${y}-${m}-${d}`\n}\n\n/**\n * HotelBookingReservation -- the React block, 1:1: a room hero (photo gallery with thumbnails,\n * rating, amenities, member rate), stay-date inputs with quick length presets, guest / room\n * steppers, toggleable upgrade rows, a special-requests input, and a sticky price breakdown\n * whose totals follow every change. Reserving shows a confirmation banner above the hero.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-hotel-booking-reservation, [ui-hotel-booking-reservation]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiCardDescriptionComponent,\n    UiCardContentComponent,\n    UiCardFooterComponent,\n    UiCheckboxComponent,\n    UiInputComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <div data-slot=\"hotel-booking-reservation\" [class]=\"rootClass\">\n      <!-- Confirmation Banner (Visible when booked) -->\n      @if (isBooked()) {\n        <div\n          class=\"border-success/30 bg-success/10 bg-success/15 relative flex flex-col gap-4 rounded-xl border p-5 sm:flex-row sm:items-center sm:justify-between\"\n        >\n          <div class=\"flex flex-wrap items-start gap-3.5\">\n            <div\n              class=\"bg-success/20 text-success mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-full\"\n            >\n              <lucide-icon [img]=\"CircleCheck\" class=\"size-5\" />\n            </div>\n            <div class=\"space-y-1\">\n              <div class=\"flex flex-wrap items-center gap-2\">\n                <h3 class=\"text-foreground text-base font-semibold\">Reservation Confirmed · Reference #RC-884920</h3>\n                <span ui-badge wrap variant=\"outline\" class=\"border-success/40 text-success text-xs font-semibold\">\n                  Guaranteed Direct\n                </span>\n              </div>\n              <p class=\"text-muted-foreground text-xs sm:text-sm\">\n                A confirmation voucher and check-in barcode have been sent to your registered email. No cancellation\n                penalty applies before {{ cancellationDeadline }}.\n              </p>\n            </div>\n          </div>\n          <button ui-button variant=\"outline\" size=\"sm\" class=\"shrink-0 font-medium\" (click)=\"isBooked.set(false)\">\n            <lucide-icon [img]=\"RotateCcw\" class=\"size-3.5\" />\n            Modify Reservation\n          </button>\n        </div>\n      }\n\n      <!-- Room Hero Header Card -->\n      <ui-card class=\"border-border overflow-hidden shadow-xs\">\n        <div class=\"grid grid-cols-1 gap-6 p-5 sm:p-6 lg:grid-cols-12 lg:gap-8\">\n          <!-- Room Photo Showcase -->\n          <div class=\"space-y-3 lg:col-span-6\">\n            <div class=\"border-border bg-muted/40 relative aspect-16/10 w-full overflow-hidden rounded-xl border\">\n              <img\n                [src]=\"activePhoto.url\"\n                [alt]=\"activePhoto.title\"\n                class=\"size-full object-cover transition-colors duration-300\"\n              />\n              <div\n                class=\"pointer-events-none absolute inset-0 bg-gradient-to-t from-black/70 via-transparent to-black/20\"\n              ></div>\n              <div class=\"absolute top-3 left-3 flex flex-wrap gap-1.5\">\n                <span\n                  ui-badge\n                  wrap\n                  variant=\"secondary\"\n                  class=\"bg-background/90 text-foreground text-xs font-medium backdrop-blur-xs\"\n                >\n                  Oceanfront View\n                </span>\n                <span\n                  ui-badge\n                  wrap\n                  variant=\"outline\"\n                  class=\"bg-background/80 text-foreground border-white/20 text-xs backdrop-blur-xs\"\n                >\n                  Floor 16\n                </span>\n              </div>\n              <div class=\"absolute top-3 right-3 flex items-center gap-1.5\">\n                <button\n                  type=\"button\"\n                  aria-label=\"Save to favorites\"\n                  class=\"bg-background/80 hover:bg-background text-foreground focus-visible:ring-ring flex size-8 items-center justify-center rounded-full backdrop-blur-xs transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                  (click)=\"isSaved.set(!isSaved())\"\n                >\n                  <lucide-icon [img]=\"Heart\" [class]=\"heartClass\" />\n                </button>\n                <button\n                  type=\"button\"\n                  aria-label=\"Share suite\"\n                  class=\"bg-background/80 hover:bg-background text-foreground focus-visible:ring-ring flex size-8 items-center justify-center rounded-full backdrop-blur-xs transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                >\n                  <lucide-icon [img]=\"Share2\" class=\"size-4\" />\n                </button>\n              </div>\n              <div class=\"absolute right-3 bottom-3 left-3 flex items-center justify-between text-xs text-white\">\n                <span class=\"font-medium drop-shadow-xs\">{{ activePhoto.title }}</span>\n                <span class=\"rounded bg-black/60 px-2 py-0.5 font-mono text-xs backdrop-blur-xs\"\n                  >{{ activePhotoIndex() + 1 }} / {{ photos.length }}</span\n                >\n              </div>\n            </div>\n\n            <!-- Mini Thumbnail Selector -->\n            <div class=\"grid grid-cols-4 gap-2\">\n              @for (photo of photos; track $index) {\n                <button\n                  type=\"button\"\n                  [attr.aria-label]=\"photo.title\"\n                  [class]=\"thumbClass($index)\"\n                  (click)=\"activePhotoIndex.set($index)\"\n                >\n                  <img [src]=\"photo.url\" [alt]=\"photo.title\" class=\"size-full object-cover\" />\n                </button>\n              }\n            </div>\n          </div>\n\n          <!-- Room Meta & Overview Information -->\n          <div class=\"flex flex-col justify-between space-y-4 lg:col-span-6\">\n            <div class=\"space-y-3\">\n              <div class=\"flex flex-wrap items-center justify-between gap-2\">\n                <div class=\"text-warning flex items-center gap-1.5 text-xs font-medium\">\n                  <lucide-icon [img]=\"Star\" class=\"fill-warning text-warning size-4\" />\n                  <span class=\"font-bold tabular-nums\">{{ rating.toFixed(1) }}</span>\n                  <span class=\"text-muted-foreground underline underline-offset-2\"\n                    >{{ reviewsCount }} verified guest reviews</span\n                  >\n                </div>\n                <span\n                  ui-badge\n                  wrap\n                  variant=\"outline\"\n                  class=\"border-primary/30 bg-primary/5 text-primary text-xs font-medium\"\n                >\n                  Official Best Rate Guarantee\n                </span>\n              </div>\n\n              <div class=\"space-y-1\">\n                <h1 class=\"text-foreground text-2xl font-bold tracking-tight sm:text-3xl\">{{ roomTitle }}</h1>\n                <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs sm:text-sm\">\n                  <lucide-icon [img]=\"Building2\" class=\"text-primary size-4 shrink-0\" />\n                  <span class=\"text-foreground font-medium\">{{ hotelName }}</span>\n                </div>\n                <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n                  <lucide-icon [img]=\"MapPin\" class=\"size-3.5 shrink-0\" />\n                  <span>{{ location }}</span>\n                </div>\n              </div>\n\n              <p class=\"text-muted-foreground text-xs leading-relaxed sm:text-sm\">\n                Expansive oceanfront sanctuary featuring custom King pillowtop bedding, floor-to-ceiling glass doors\n                opening onto an oversized private balcony with sweeping Atlantic panoramas, freestanding soaking tub,\n                and bespoke in-room dining.\n              </p>\n\n              <!-- Room Key Amenities Grid -->\n              <div class=\"grid grid-cols-1 gap-2 pt-1 sm:grid-cols-2 md:grid-cols-3\">\n                <div class=\"border-border/60 bg-muted/20 flex items-center gap-2 rounded-md border p-2 text-xs\">\n                  <lucide-icon [img]=\"Maximize2\" class=\"text-muted-foreground size-3.5 shrink-0\" />\n                  <span class=\"text-foreground font-medium\">680 sq ft / 63 m²</span>\n                </div>\n                <div class=\"border-border/60 bg-muted/20 flex items-center gap-2 rounded-md border p-2 text-xs\">\n                  <lucide-icon [img]=\"BedDouble\" class=\"text-muted-foreground size-3.5 shrink-0\" />\n                  <span class=\"text-foreground font-medium\">1 King Bed</span>\n                </div>\n                <div class=\"border-border/60 bg-muted/20 flex items-center gap-2 rounded-md border p-2 text-xs\">\n                  <lucide-icon [img]=\"Waves\" class=\"text-muted-foreground size-3.5 shrink-0\" />\n                  <span class=\"text-foreground font-medium\">Ocean Balcony</span>\n                </div>\n                <div class=\"border-border/60 bg-muted/20 flex items-center gap-2 rounded-md border p-2 text-xs\">\n                  <lucide-icon [img]=\"Bath\" class=\"text-muted-foreground size-3.5 shrink-0\" />\n                  <span class=\"text-foreground font-medium\">Marble Soaking Tub</span>\n                </div>\n                <div class=\"border-border/60 bg-muted/20 flex items-center gap-2 rounded-md border p-2 text-xs\">\n                  <lucide-icon [img]=\"Wifi\" class=\"text-muted-foreground size-3.5 shrink-0\" />\n                  <span class=\"text-foreground font-medium\">Fast Fiber WiFi</span>\n                </div>\n                <div class=\"border-border/60 bg-muted/20 flex items-center gap-2 rounded-md border p-2 text-xs\">\n                  <lucide-icon [img]=\"Coffee\" class=\"text-muted-foreground size-3.5 shrink-0\" />\n                  <span class=\"text-foreground font-medium\">Nespresso Machine</span>\n                </div>\n              </div>\n            </div>\n\n            <div\n              class=\"border-border/80 bg-muted/30 flex flex-wrap items-center justify-between gap-3 rounded-xl border p-3.5 sm:p-4\"\n            >\n              <div>\n                <span class=\"text-muted-foreground block text-xs font-medium tracking-wider uppercase\">\n                  Direct Member Rate\n                </span>\n                <div class=\"flex flex-wrap items-baseline gap-2\">\n                  <span class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl\">\n                    {{ formatCurrency(basePrice) }}\n                  </span>\n                  <span class=\"text-muted-foreground text-xs font-normal sm:text-sm\">/ night</span>\n                  <span class=\"text-muted-foreground text-xs tabular-nums line-through sm:text-sm\">$560.00</span>\n                </div>\n              </div>\n              <span\n                ui-badge\n                wrap\n                variant=\"secondary\"\n                class=\"border-success/20 bg-success/10 text-success text-xs font-semibold\"\n              >\n                Save 13% Direct\n              </span>\n            </div>\n          </div>\n        </div>\n      </ui-card>\n\n      <!-- Interactive 2-Column Booking Configuration & Price Breakdown Grid -->\n      <div class=\"grid grid-cols-1 gap-8 lg:grid-cols-12 lg:items-start\">\n        <!-- Left Column: Reservation Inputs, Guests & Upgrades -->\n        <div class=\"space-y-6 lg:col-span-7\">\n          <!-- Stay Dates Selection -->\n          <ui-card class=\"shadow-xs\">\n            <ui-card-header>\n              <div class=\"flex items-center justify-between\">\n                <div class=\"space-y-0.5\">\n                  <ui-card-title class=\"text-lg\">Stay Dates &amp; Duration</ui-card-title>\n                  <ui-card-description>\n                    Select your check-in and check-out dates to calculate live availability.\n                  </ui-card-description>\n                </div>\n                <span ui-badge wrap variant=\"outline\" class=\"gap-1 text-xs font-medium\">\n                  <lucide-icon [img]=\"CalendarDays\" class=\"text-primary size-3.5\" />\n                  {{ nights }} {{ nights === 1 ? 'Night' : 'Nights' }}\n                </span>\n              </div>\n            </ui-card-header>\n            <ui-card-content class=\"space-y-5\">\n              <!-- Date Inputs -->\n              <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n                <div class=\"space-y-2\">\n                  <label for=\"check-in-date\" class=\"text-foreground flex items-center gap-1.5 text-xs font-semibold\">\n                    <lucide-icon [img]=\"Calendar\" class=\"text-primary size-3.5\" />\n                    Check-in Date (3:00 PM)\n                  </label>\n                  <ui-input\n                    id=\"check-in-date\"\n                    type=\"date\"\n                    [value]=\"checkInDate()\"\n                    (valueChange)=\"checkInDate.set($event)\"\n                  />\n                </div>\n\n                <div class=\"space-y-2\">\n                  <label for=\"check-out-date\" class=\"text-foreground flex items-center gap-1.5 text-xs font-semibold\">\n                    <lucide-icon [img]=\"Calendar\" class=\"text-primary size-3.5\" />\n                    Check-out Date (12:00 PM)\n                  </label>\n                  <ui-input\n                    id=\"check-out-date\"\n                    type=\"date\"\n                    [value]=\"checkOutDate()\"\n                    (valueChange)=\"checkOutDate.set($event)\"\n                  />\n                </div>\n              </div>\n\n              <!-- Stay Summary Highlight & Quick Length Presets -->\n              <div\n                class=\"border-border/60 bg-muted/30 flex flex-col gap-3 rounded-lg border p-3 sm:flex-row sm:items-center sm:justify-between\"\n              >\n                <div class=\"text-foreground text-xs font-medium\">\n                  <span class=\"text-muted-foreground\">Selected Stay: </span>\n                  <span class=\"font-semibold\">{{ formattedRange }}</span>\n                </div>\n                <div class=\"flex flex-wrap items-center gap-1.5\">\n                  <span class=\"text-muted-foreground mr-1 text-xs\">Quick:</span>\n                  @for (preset of presets; track preset) {\n                    <button\n                      ui-button\n                      variant=\"outline\"\n                      size=\"xs\"\n                      [class]=\"presetClass(preset)\"\n                      (click)=\"setDuration(preset)\"\n                    >\n                      {{ preset }}N\n                    </button>\n                  }\n                </div>\n              </div>\n            </ui-card-content>\n          </ui-card>\n\n          <!-- Guests & Rooms Steppers -->\n          <ui-card class=\"shadow-xs\">\n            <ui-card-header>\n              <ui-card-title class=\"text-lg\">Guests &amp; Rooms</ui-card-title>\n              <ui-card-description>Configure the number of adults, children, and suites requested.</ui-card-description>\n            </ui-card-header>\n            <ui-card-content class=\"space-y-4\">\n              <!-- Adults Stepper -->\n              <div class=\"border-border/60 flex flex-wrap items-center justify-between rounded-lg border p-3.5\">\n                <div class=\"flex items-center gap-3\">\n                  <div\n                    class=\"bg-primary/10 text-primary border-primary/20 flex size-9 shrink-0 items-center justify-center rounded-lg border\"\n                  >\n                    <lucide-icon [img]=\"Users\" class=\"size-4\" />\n                  </div>\n                  <div class=\"space-y-0.5\">\n                    <span class=\"text-foreground text-sm font-medium\">Adults</span>\n                    <p class=\"text-muted-foreground text-xs\">Ages 13 and above</p>\n                  </div>\n                </div>\n                <div class=\"flex items-center gap-3\">\n                  <button\n                    ui-button\n                    variant=\"outline\"\n                    size=\"icon-sm\"\n                    [disabled]=\"adults() <= 1\"\n                    aria-label=\"Decrease adults\"\n                    (click)=\"updateAdults(-1)\"\n                  >\n                    <lucide-icon [img]=\"Minus\" class=\"size-3.5\" />\n                  </button>\n                  <span class=\"text-foreground w-6 text-center text-sm font-bold tabular-nums\">{{ adults() }}</span>\n                  <button\n                    ui-button\n                    variant=\"outline\"\n                    size=\"icon-sm\"\n                    [disabled]=\"adults() >= 8\"\n                    aria-label=\"Increase adults\"\n                    (click)=\"updateAdults(1)\"\n                  >\n                    <lucide-icon [img]=\"Plus\" class=\"size-3.5\" />\n                  </button>\n                </div>\n              </div>\n\n              <!-- Children Stepper -->\n              <div class=\"border-border/60 flex flex-wrap items-center justify-between rounded-lg border p-3.5\">\n                <div class=\"flex items-center gap-3\">\n                  <div\n                    class=\"bg-primary/10 text-primary border-primary/20 flex size-9 shrink-0 items-center justify-center rounded-lg border\"\n                  >\n                    <lucide-icon [img]=\"Baby\" class=\"size-4\" />\n                  </div>\n                  <div class=\"space-y-0.5\">\n                    <span class=\"text-foreground text-sm font-medium\">Children</span>\n                    <p class=\"text-muted-foreground text-xs\">Ages 0 to 12 years</p>\n                  </div>\n                </div>\n                <div class=\"flex items-center gap-3\">\n                  <button\n                    ui-button\n                    variant=\"outline\"\n                    size=\"icon-sm\"\n                    [disabled]=\"children() <= 0\"\n                    aria-label=\"Decrease children\"\n                    (click)=\"updateChildren(-1)\"\n                  >\n                    <lucide-icon [img]=\"Minus\" class=\"size-3.5\" />\n                  </button>\n                  <span class=\"text-foreground w-6 text-center text-sm font-bold tabular-nums\">{{ children() }}</span>\n                  <button\n                    ui-button\n                    variant=\"outline\"\n                    size=\"icon-sm\"\n                    [disabled]=\"children() >= 6\"\n                    aria-label=\"Increase children\"\n                    (click)=\"updateChildren(1)\"\n                  >\n                    <lucide-icon [img]=\"Plus\" class=\"size-3.5\" />\n                  </button>\n                </div>\n              </div>\n\n              <!-- Rooms Stepper -->\n              <div class=\"border-border/60 flex flex-wrap items-center justify-between rounded-lg border p-3.5\">\n                <div class=\"flex items-center gap-3\">\n                  <div\n                    class=\"bg-primary/10 text-primary border-primary/20 flex size-9 shrink-0 items-center justify-center rounded-lg border\"\n                  >\n                    <lucide-icon [img]=\"BedDouble\" class=\"size-4\" />\n                  </div>\n                  <div class=\"space-y-0.5\">\n                    <span class=\"text-foreground text-sm font-medium\">Suites / Rooms</span>\n                    <p class=\"text-muted-foreground text-xs\">Max 4 guests per suite</p>\n                  </div>\n                </div>\n                <div class=\"flex items-center gap-3\">\n                  <button\n                    ui-button\n                    variant=\"outline\"\n                    size=\"icon-sm\"\n                    [disabled]=\"rooms() <= 1\"\n                    aria-label=\"Decrease rooms\"\n                    (click)=\"updateRooms(-1)\"\n                  >\n                    <lucide-icon [img]=\"Minus\" class=\"size-3.5\" />\n                  </button>\n                  <span class=\"text-foreground w-6 text-center text-sm font-bold tabular-nums\">{{ rooms() }}</span>\n                  <button\n                    ui-button\n                    variant=\"outline\"\n                    size=\"icon-sm\"\n                    [disabled]=\"rooms() >= 4\"\n                    aria-label=\"Increase rooms\"\n                    (click)=\"updateRooms(1)\"\n                  >\n                    <lucide-icon [img]=\"Plus\" class=\"size-3.5\" />\n                  </button>\n                </div>\n              </div>\n            </ui-card-content>\n          </ui-card>\n\n          <!-- Room Upgrades & Add-ons -->\n          <ui-card class=\"shadow-xs\">\n            <ui-card-header>\n              <div class=\"flex items-center justify-between\">\n                <div class=\"space-y-0.5\">\n                  <ui-card-title class=\"text-lg\">Room Upgrades &amp; Exclusive Services</ui-card-title>\n                  <ui-card-description>\n                    Tailor your stay with luxury amenities and curated hotel enhancements.\n                  </ui-card-description>\n                </div>\n                <span ui-badge wrap variant=\"secondary\" class=\"text-xs\">{{ selectedUpgrades().length }} Selected</span>\n              </div>\n            </ui-card-header>\n            <ui-card-content class=\"space-y-3\">\n              <!-- Upgrade 1: Oceanfront Balcony -->\n              <div\n                role=\"button\"\n                tabindex=\"0\"\n                [class]=\"upgradeClass('balcony')\"\n                (click)=\"toggleUpgrade('balcony')\"\n                (keydown)=\"onUpgradeKeydown($event, 'balcony')\"\n              >\n                <div class=\"flex flex-wrap items-start gap-3.5\">\n                  <ui-checkbox id=\"upgrade-balcony\" [checked]=\"has('balcony')\" class=\"pointer-events-none mt-1\" />\n                  <div class=\"space-y-1\">\n                    <div class=\"flex flex-wrap items-center gap-2\">\n                      <span class=\"text-foreground text-sm font-semibold\">Oceanfront High-Floor Balcony</span>\n                      <span ui-badge wrap variant=\"outline\" class=\"text-xs font-medium\">+$50 / night</span>\n                    </div>\n                    <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                      Guaranteed unobstructed 180° Atlantic Ocean sunrise view on floors 15+ with premium teak deck\n                      loungers.\n                    </p>\n                  </div>\n                </div>\n                <div class=\"shrink-0 text-right\">\n                  <span class=\"text-foreground text-sm font-bold tabular-nums\"\n                    >+{{ formatCurrency(50 * nights * rooms()) }}</span\n                  >\n                  <span class=\"text-muted-foreground block text-xs\">for {{ nights }} nights</span>\n                </div>\n              </div>\n\n              <!-- Upgrade 2: Gourmet Breakfast -->\n              <div\n                role=\"button\"\n                tabindex=\"0\"\n                [class]=\"upgradeClass('breakfast')\"\n                (click)=\"toggleUpgrade('breakfast')\"\n                (keydown)=\"onUpgradeKeydown($event, 'breakfast')\"\n              >\n                <div class=\"flex flex-wrap items-start gap-3.5\">\n                  <ui-checkbox id=\"upgrade-breakfast\" [checked]=\"has('breakfast')\" class=\"pointer-events-none mt-1\" />\n                  <div class=\"space-y-1\">\n                    <div class=\"flex flex-wrap items-center gap-2\">\n                      <span class=\"text-foreground text-sm font-semibold\">Daily Gourmet Breakfast</span>\n                      <span ui-badge wrap variant=\"outline\" class=\"text-xs font-medium\">+$35 / person / day</span>\n                    </div>\n                    <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                      Artisanal morning buffet at Artisan Beach House or private veranda in-room breakfast with fresh\n                      juices.\n                    </p>\n                    <!-- Guest selection when active -->\n                    @if (has('breakfast')) {\n                      <div class=\"flex flex-wrap items-center gap-2 pt-1.5 text-xs\" (click)=\"$event.stopPropagation()\">\n                        <span class=\"text-muted-foreground\">Breakfast plan for:</span>\n                        <div class=\"flex flex-wrap items-center gap-1.5\">\n                          @for (count of breakfastOptions; track count) {\n                            <button\n                              ui-button\n                              variant=\"outline\"\n                              size=\"xs\"\n                              [class]=\"breakfastClass(count)\"\n                              (click)=\"breakfastGuests.set(count)\"\n                            >\n                              {{ count }} {{ count === 1 ? 'Guest' : 'Guests' }}\n                            </button>\n                          }\n                        </div>\n                      </div>\n                    }\n                  </div>\n                </div>\n                <div class=\"shrink-0 text-right\">\n                  <span class=\"text-foreground text-sm font-bold tabular-nums\"\n                    >+{{ formatCurrency(35 * breakfastGuests() * nights) }}</span\n                  >\n                  <span class=\"text-muted-foreground block text-xs\"\n                    >{{ breakfastGuests() }} {{ breakfastGuests() === 1 ? 'guest' : 'guests' }} · {{ nights }}d</span\n                  >\n                </div>\n              </div>\n\n              <!-- Upgrade 3: Airport Chauffeur -->\n              <div\n                role=\"button\"\n                tabindex=\"0\"\n                [class]=\"upgradeClass('chauffeur')\"\n                (click)=\"toggleUpgrade('chauffeur')\"\n                (keydown)=\"onUpgradeKeydown($event, 'chauffeur')\"\n              >\n                <div class=\"flex flex-wrap items-start gap-3.5\">\n                  <ui-checkbox id=\"upgrade-chauffeur\" [checked]=\"has('chauffeur')\" class=\"pointer-events-none mt-1\" />\n                  <div class=\"space-y-1\">\n                    <div class=\"flex flex-wrap items-center gap-2\">\n                      <span class=\"text-foreground text-sm font-semibold\">Private Airport Luxury Chauffeur</span>\n                      <span ui-badge wrap variant=\"outline\" class=\"text-xs font-medium\">+$120 one-time</span>\n                    </div>\n                    <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                      Curbside meet-and-greet with dedicated Cadillac Escalade or Mercedes S-Class transfer from MIA or\n                      FLL.\n                    </p>\n                  </div>\n                </div>\n                <div class=\"shrink-0 text-right\">\n                  <span class=\"text-foreground text-sm font-bold tabular-nums\">+$120.00</span>\n                  <span class=\"text-muted-foreground block text-xs\">flat one-time</span>\n                </div>\n              </div>\n            </ui-card-content>\n          </ui-card>\n\n          <!-- Special Requests Card -->\n          <ui-card class=\"shadow-xs\">\n            <ui-card-header class=\"pb-3\">\n              <ui-card-title class=\"text-base\">Special Requests &amp; Arrival Notes</ui-card-title>\n              <ui-card-description>\n                Let us know if you are celebrating a special occasion or require early check-in.\n              </ui-card-description>\n            </ui-card-header>\n            <ui-card-content>\n              <ui-input\n                id=\"special-requests\"\n                [value]=\"specialRequests()\"\n                (valueChange)=\"specialRequests.set($event)\"\n                placeholder=\"e.g., Honeymoon celebration, feather-free bedding, quiet high floor...\"\n                [maxLength]=\"200\"\n                showCount\n              />\n            </ui-card-content>\n          </ui-card>\n        </div>\n\n        <!-- Right Column: Total Price Breakdown & Checkout Card (Sticky) -->\n        <div class=\"lg:sticky lg:top-8 lg:col-span-5\">\n          <ui-card class=\"border-border bg-card shadow-xs\">\n            <ui-card-header class=\"pb-4\">\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">\n                  Stay Price Breakdown\n                </span>\n                <span\n                  ui-badge\n                  wrap\n                  variant=\"secondary\"\n                  class=\"bg-primary/10 text-primary border-primary/20 gap-1 text-xs font-medium\"\n                >\n                  <lucide-icon [img]=\"CircleCheck\" class=\"size-3\" />\n                  Instant Confirmation\n                </span>\n              </div>\n\n              <div class=\"mt-3 space-y-1\">\n                <div class=\"text-muted-foreground text-xs\">Total Stay Price (USD)</div>\n                <div class=\"flex items-baseline gap-2\">\n                  <span class=\"text-foreground text-3xl font-bold tracking-tight tabular-nums sm:text-4xl\">\n                    {{ formatCurrency(totalStayPrice) }}\n                  </span>\n                </div>\n                <p class=\"text-muted-foreground text-xs\">\n                  Total for <span class=\"text-foreground font-medium\">{{ nights }} nights</span> · {{ rooms() }} suite ·\n                  {{ adults() + children() }} guests\n                </p>\n              </div>\n            </ui-card-header>\n\n            <ui-card-content class=\"space-y-4\">\n              <ui-separator />\n\n              <!-- Itemized Pricing Breakdown -->\n              <div class=\"space-y-2.5\">\n                <!-- Base Room Subtotal -->\n                <div class=\"flex items-center justify-between text-sm\">\n                  <span class=\"text-muted-foreground\"\n                    >Room subtotal ({{ formatCurrency(basePrice) }} × {{ nights }} {{ nights === 1 ? 'night' : 'nights'\n                    }}{{ rooms() > 1 ? ' × ' + rooms() + ' rooms' : '' }})</span\n                  >\n                  <span class=\"text-foreground font-semibold tabular-nums\">{{ formatCurrency(roomSubtotal) }}</span>\n                </div>\n\n                <!-- Selected Add-ons summary / itemized -->\n                <div class=\"space-y-1.5\">\n                  <div class=\"flex items-center justify-between text-sm\">\n                    <span class=\"text-muted-foreground\">Selected Add-ons ({{ selectedUpgrades().length }} active)</span>\n                    <span class=\"text-foreground font-semibold tabular-nums\">\n                      {{ formatCurrency(selectedAddonsTotal) }}\n                    </span>\n                  </div>\n\n                  <!-- Addon sub-items -->\n                  @if (selectedUpgrades().length > 0) {\n                    <div class=\"border-border/60 bg-muted/20 space-y-1 rounded-md border p-2.5 text-xs\">\n                      @if (has('balcony')) {\n                        <div class=\"text-muted-foreground flex items-center justify-between\">\n                          <span>• Oceanfront Balcony ({{ formatCurrency(50) }} × {{ nights }}n)</span>\n                          <span class=\"text-foreground font-medium tabular-nums\">{{\n                            formatCurrency(balconyCost)\n                          }}</span>\n                        </div>\n                      }\n                      @if (has('breakfast')) {\n                        <div class=\"text-muted-foreground flex items-center justify-between\">\n                          <span\n                            >• Gourmet Breakfast ({{ formatCurrency(35) }} × {{ breakfastGuests() }}g ×\n                            {{ nights }}d)</span\n                          >\n                          <span class=\"text-foreground font-medium tabular-nums\">{{\n                            formatCurrency(breakfastCost)\n                          }}</span>\n                        </div>\n                      }\n                      @if (has('chauffeur')) {\n                        <div class=\"text-muted-foreground flex items-center justify-between\">\n                          <span>• Luxury Airport Chauffeur (one-time)</span>\n                          <span class=\"text-foreground font-medium tabular-nums\">{{\n                            formatCurrency(chauffeurCost)\n                          }}</span>\n                        </div>\n                      }\n                      @if (has('spa')) {\n                        <div class=\"text-muted-foreground flex items-center justify-between\">\n                          <span>• Thermal Spa Pass ({{ formatCurrency(65) }} × {{ adults() }} guests)</span>\n                          <span class=\"text-foreground font-medium tabular-nums\">{{ formatCurrency(spaCost) }}</span>\n                        </div>\n                      }\n                    </div>\n                  }\n                </div>\n\n                <!-- Resort Fee -->\n                <div class=\"flex items-center justify-between text-sm\">\n                  <div class=\"flex items-center gap-1\">\n                    <span class=\"text-muted-foreground\">Resort &amp; Hospitality Fee</span>\n                    <lucide-icon [img]=\"Info\" class=\"text-muted-foreground/70 size-3.5 cursor-help\" />\n                  </div>\n                  <span class=\"text-foreground font-semibold tabular-nums\">{{ formatCurrency(resortFee) }}</span>\n                </div>\n                <div class=\"text-muted-foreground pl-0.5 text-xs\">\n                  $30.00 / night · Beach loungers, high-speed WiFi, welcome champagne\n                </div>\n\n                <!-- Estimated Taxes -->\n                <div class=\"flex items-center justify-between text-sm\">\n                  <span class=\"text-muted-foreground\">Estimated Taxes &amp; Tourism Surcharge (14%)</span>\n                  <span class=\"text-foreground font-semibold tabular-nums\">{{ formatCurrency(estimatedTaxes) }}</span>\n                </div>\n              </div>\n\n              <ui-separator />\n\n              <!-- Cancellation Policy Banner -->\n              <div class=\"border-success/20 bg-success/10 bg-success/15 space-y-1 rounded-xl border p-3.5\">\n                <div class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"ShieldCheck\" class=\"text-success size-4 shrink-0\" />\n                  <span class=\"text-foreground text-xs font-semibold\">\n                    Free cancellation until 48 hours before check-in\n                  </span>\n                </div>\n                <p class=\"text-muted-foreground pl-6 text-xs leading-normal\">\n                  Cancel before <span class=\"text-foreground font-medium\">{{ cancellationDeadline }}</span> for a 100%\n                  full refund with zero fees.\n                </p>\n              </div>\n\n              <!-- Trust Bullet Points -->\n              <ul class=\"text-muted-foreground space-y-2 text-xs\">\n                <li class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Check\" class=\"text-primary size-3.5 shrink-0\" />\n                  <span>No upfront booking fees or surprise service surcharges</span>\n                </li>\n                <li class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Check\" class=\"text-primary size-3.5 shrink-0\" />\n                  <span>Official Ritz-Carlton direct member rewards applied</span>\n                </li>\n                <li class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Lock\" class=\"text-primary size-3.5 shrink-0\" />\n                  <span>256-bit encrypted secure bank checkout</span>\n                </li>\n              </ul>\n            </ui-card-content>\n\n            <ui-card-footer class=\"flex flex-col gap-2.5 pt-2\">\n              <button ui-button class=\"w-full gap-2 font-semibold shadow-xs\" size=\"lg\" (click)=\"book()\">\n                <lucide-icon [img]=\"CreditCard\" class=\"size-4\" />\n                <span>Reserve &amp; Pay Now ({{ formatCurrency(totalStayPrice) }})</span>\n              </button>\n              <button ui-button variant=\"outline\" class=\"w-full text-xs font-medium\" size=\"default\" (click)=\"book()\">\n                Hold Room · Pay at Check-in\n              </button>\n              <p class=\"text-muted-foreground text-center text-xs\">\n                You won't be charged yet. Final confirmation is sent instantly.\n              </p>\n            </ui-card-footer>\n          </ui-card>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiHotelBookingReservationComponent implements OnInit {\n  @Input('class') className?: string\n  @Input() roomTitle = 'Deluxe Ocean View King Suite'\n  @Input() hotelName = 'The Ritz-Carlton Bal Harbour, Miami'\n  @Input() location = 'Bal Harbour, Miami Beach, FL'\n  @Input() rating = 5.0\n  @Input() reviewsCount = 120\n  @Input() basePrice = 485\n  @Input() initialCheckIn = '2026-08-24'\n  @Input() initialCheckOut = '2026-08-29'\n  @Input() initialAdults = 2\n  @Input() initialChildren = 1\n  @Input() initialRooms = 1\n\n  readonly checkInDate = signal('')\n  readonly checkOutDate = signal('')\n  readonly adults = signal(2)\n  readonly children = signal(1)\n  readonly rooms = signal(1)\n  readonly specialRequests = signal('')\n  readonly isBooked = signal(false)\n  readonly isSaved = signal(false)\n  readonly activePhotoIndex = signal(0)\n\n  // Upgrades state: Oceanfront balcony and Daily Breakfast are checked by default\n  readonly selectedUpgrades = signal<HotelBookingUpgrade[]>(['balcony', 'breakfast'])\n  readonly breakfastGuests = signal(1)\n\n  protected readonly photos = roomPhotos\n  protected readonly presets = [3, 5, 7, 10]\n  protected readonly breakfastOptions = [1, 2]\n\n  protected readonly Baby = Baby\n  protected readonly Bath = Bath\n  protected readonly BedDouble = BedDouble\n  protected readonly Building2 = Building2\n  protected readonly Calendar = Calendar\n  protected readonly CalendarDays = CalendarDays\n  protected readonly Check = Check\n  protected readonly CircleCheck = CircleCheck\n  protected readonly Coffee = Coffee\n  protected readonly CreditCard = CreditCard\n  protected readonly Heart = Heart\n  protected readonly Info = Info\n  protected readonly Lock = Lock\n  protected readonly MapPin = MapPin\n  protected readonly Maximize2 = Maximize2\n  protected readonly Minus = Minus\n  protected readonly Plus = Plus\n  protected readonly RotateCcw = RotateCcw\n  protected readonly Share2 = Share2\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Star = Star\n  protected readonly Users = Users\n  protected readonly Waves = Waves\n  protected readonly Wifi = Wifi\n\n  ngOnInit(): void {\n    // React seeds its useState once from the initial* props; inputs land after construction.\n    this.checkInDate.set(this.initialCheckIn)\n    this.checkOutDate.set(this.initialCheckOut)\n    this.adults.set(this.initialAdults)\n    this.children.set(this.initialChildren)\n    this.rooms.set(this.initialRooms)\n  }\n\n  protected get rootClass(): string {\n    return cn('mx-auto w-full max-w-6xl space-y-8 p-4 sm:p-6 lg:p-8', this.className)\n  }\n\n  protected get activePhoto(): HotelBookingPhoto {\n    return this.photos[this.activePhotoIndex()] ?? this.photos[0]!\n  }\n\n  has(id: HotelBookingUpgrade): boolean {\n    return this.selectedUpgrades().includes(id)\n  }\n\n  toggleUpgrade(id: HotelBookingUpgrade): void {\n    this.selectedUpgrades.update((prev) => (prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]))\n  }\n\n  onUpgradeKeydown(event: KeyboardEvent, id: HotelBookingUpgrade): void {\n    if (event.key === ' ' || event.key === 'Enter') {\n      event.preventDefault()\n      this.toggleUpgrade(id)\n    }\n  }\n\n  // Date calculations\n  get nights(): number {\n    const checkIn = this.checkInDate()\n    const checkOut = this.checkOutDate()\n    if (!checkIn || !checkOut) return 5\n    const start = new Date(checkIn).getTime()\n    const end = new Date(checkOut).getTime()\n    const diffDays = Math.round((end - start) / (1000 * 60 * 60 * 24))\n    return diffDays > 0 ? diffDays : 1\n  }\n\n  setDuration(presetNights: number): void {\n    const checkIn = this.checkInDate()\n    if (!checkIn) return\n    const start = new Date(checkIn + 'T00:00:00')\n    start.setDate(start.getDate() + presetNights)\n    this.checkOutDate.set(toIsoDate(start))\n  }\n\n  formatDateDisplay(dateStr: string): string {\n    if (!dateStr) return ''\n    const [year, month, day] = dateStr.split('-').map(Number)\n    if (!year || !month || !day) return dateStr\n    const date = new Date(year, month - 1, day)\n    return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n  }\n\n  get formattedRange(): string {\n    const nights = this.nights\n    return `${this.formatDateDisplay(this.checkInDate())} → ${this.formatDateDisplay(this.checkOutDate())} · ${nights} ${nights === 1 ? 'Night' : 'Nights'}`\n  }\n\n  get cancellationDeadline(): string {\n    const checkIn = this.checkInDate()\n    if (!checkIn) return '48 hours prior to arrival'\n    const start = new Date(checkIn + 'T00:00:00')\n    start.setDate(start.getDate() - 2)\n    return this.formatDateDisplay(toIsoDate(start))\n  }\n\n  // Financial calculations\n  get roomSubtotal(): number {\n    return this.basePrice * this.nights * this.rooms()\n  }\n  get balconyCost(): number {\n    return this.has('balcony') ? 50 * this.nights * this.rooms() : 0\n  }\n  get breakfastCost(): number {\n    return this.has('breakfast') ? 35 * this.breakfastGuests() * this.nights : 0\n  }\n  get chauffeurCost(): number {\n    return this.has('chauffeur') ? 120 : 0\n  }\n  get spaCost(): number {\n    return this.has('spa') ? 65 * this.adults() : 0\n  }\n  get selectedAddonsTotal(): number {\n    return this.balconyCost + this.breakfastCost + this.chauffeurCost + this.spaCost\n  }\n  get resortFee(): number {\n    return 30 * this.nights * this.rooms()\n  }\n  get taxableSubtotal(): number {\n    return this.roomSubtotal + this.selectedAddonsTotal + this.resortFee\n  }\n  get estimatedTaxes(): number {\n    return Math.round(this.taxableSubtotal * 0.14 * 100) / 100\n  }\n  get totalStayPrice(): number {\n    return this.taxableSubtotal + this.estimatedTaxes\n  }\n\n  formatCurrency(val: number): string {\n    return currency.format(val)\n  }\n\n  // Stepper modifiers\n  updateAdults(delta: number): void {\n    const next = this.adults() + delta\n    if (next >= 1 && next <= 8) {\n      if (this.breakfastGuests() > next) this.breakfastGuests.set(next)\n      this.adults.set(next)\n    }\n  }\n\n  updateChildren(delta: number): void {\n    const next = this.children() + delta\n    if (next >= 0 && next <= 6) this.children.set(next)\n  }\n\n  updateRooms(delta: number): void {\n    const next = this.rooms() + delta\n    if (next >= 1 && next <= 4) this.rooms.set(next)\n  }\n\n  book(): void {\n    this.isBooked.set(true)\n  }\n\n  protected get heartClass(): string {\n    return cn('size-4 transition-colors', this.isSaved() ? 'fill-destructive text-destructive' : 'text-foreground')\n  }\n\n  protected thumbClass(idx: number): string {\n    return cn(\n      'border-border relative aspect-16/10 overflow-hidden rounded-lg border transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none',\n      this.activePhotoIndex() === idx ? 'ring-primary border-primary ring-2' : 'opacity-70 hover:opacity-100',\n    )\n  }\n\n  protected presetClass(preset: number): string {\n    return this.nights === preset ? 'bg-primary text-primary-foreground border-primary hover:bg-primary/90' : ''\n  }\n\n  protected breakfastClass(count: number): string {\n    return this.breakfastGuests() === count ? 'bg-primary text-primary-foreground border-primary' : ''\n  }\n\n  protected upgradeClass(id: HotelBookingUpgrade): string {\n    return cn(\n      'border-border focus-visible:ring-ring flex cursor-pointer items-start justify-between gap-4 rounded-xl border p-4 transition-colors select-none focus-visible:ring-2 focus-visible:outline-none',\n      this.has(id)\n        ? 'border-primary/40 bg-primary/5 shadow-xs'\n        : 'bg-card hover:border-border/80 opacity-80 hover:opacity-100',\n    )\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/hotel-booking-reservation/hotel-booking-reservation.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/hotel-booking-reservation/index.ts",
      "content": "export {\n  UiHotelBookingReservationComponent,\n  type HotelBookingPhoto,\n  type HotelBookingUpgrade,\n} from './hotel-booking-reservation.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/hotel-booking-reservation/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/checkbox.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/separator.json"
  ],
  "description": "Luxury hotel room booking and reservation widget with interactive date range picker, guest & room steppers, room upgrade checkboxes, live reactive pricing breakdown, and instant reservation checkout.",
  "categories": [
    "commerce",
    "hospitality",
    "booking",
    "ecommerce",
    "app"
  ]
}