{"id":719,"date":"2025-11-24T20:51:44","date_gmt":"2025-11-24T20:51:44","guid":{"rendered":"https:\/\/wcaa.preprod.fruitionqa.com\/?page_id=719"},"modified":"2026-03-31T16:51:20","modified_gmt":"2026-03-31T20:51:20","slug":"parking","status":"publish","type":"page","link":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/parking-and-transportation\/parking\/","title":{"rendered":"Parking"},"content":{"rendered":"<div class=\"blockGlobal paddingTopMD paddingBottomLG heroFluid bottomLeftRadius bottomRightRadius\" style=\"background-image:url(https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/arriving-01-scaled.webp);background-size:cover;background-position:center center;\">\n\t<img decoding=\"async\" src=\"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/themes\/wcaa\/images\/GraphicSwoop2.svg\" class=\"graphicSwoopDouble\" alt=\"\"><div class=\"overlay\"><\/div>\t<div class=\"container\">\n\t\t<div class=\"grid\">\n\t\t\t\t\t\t\t<div class=\"col-sm-10 col-md-8\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"breadcrumbsStyle ghost dark left\"><p id=\"breadcrumbs\"><span><span><a href=\"https:\/\/wcaa.preprod.fruitionqa.com\/\">Home<\/a><\/span><\/span><\/p><\/div>\t\t\t\t\n\n<div class=\"wysiwyg \">\n\t<div class=\"maincopy dark\">\n\t\t\t\t\t<h1>Parking<\/h1>\n\t\t\t\t\t<\/div>\n\n\t\n\t<\/div>\n\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n<div class='jumpNavWrapper'><div class='container'>\n<div class=\"jumpNavigation\" data-select-id=\"jumpnavigation_8418\">\n\t\t\t\n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"#parkingoptions\">\n\n\t\n\t<span class=\"linksTitle\">Options and Pricing<\/span>\n    \n\t<\/a>\n\t\t\t\n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"#parkingpolicies\">\n\n\t\n\t<span class=\"linksTitle\">Parking Policies<\/span>\n    \n\t<\/a>\n\t\t\t\n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"#parkingreceiptrequestform\">\n\n\t\n\t<span class=\"linksTitle\">Request Parking Receipt<\/span>\n    \n\t<\/a>\n\t\t\t\n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"#accessibilityinformation\">\n\n\t\n\t<span class=\"linksTitle\">Accessibility<\/span>\n    \n\t<\/a>\n\t\t\t\n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"#frequentlyaskedquestions\">\n\n\t\n\t<span class=\"linksTitle\">FAQs<\/span>\n    \n\t<\/a>\n\t\t\t\n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"#contactus\">\n\n\t\n\t<span class=\"linksTitle\">Contact Us<\/span>\n    \n\t<\/a>\n\t\n\t\n<div class=\"dropdown-container\">\n    \n  <div class=\"dropdown-wrapper dropdown-jumpnavigation_8418_dropdown_6799  \">\n\t<select id=\"jumpnavigation_8418_dropdown_6799\"   aria-label=\"Jump Link\">\n\t  \t\t<option value=\"#parkingoptions\">Options and Pricing<\/option>\n\t  \t\t<option value=\"#parkingpolicies\">Parking Policies<\/option>\n\t  \t\t<option value=\"#parkingreceiptrequestform\">Request Parking Receipt<\/option>\n\t  \t\t<option value=\"#accessibilityinformation\">Accessibility<\/option>\n\t  \t\t<option value=\"#frequentlyaskedquestions\">FAQs<\/option>\n\t  \t\t<option value=\"#contactus\">Contact Us<\/option>\n\t  \t<\/select>\n  <\/div>\n<\/div>\n\n\n<script>\n  const jumpnavigation_8418_dropdown_6799 = initChoices('#jumpnavigation_8418_dropdown_6799', {\n  removeItemButton: false,\n  placeholder: true,\n  placeholderValue: 'Jump Link',\n  searchEnabled: false,\n  shouldSort: false,\n  itemSelectText: '',\n});\n<\/script>\n<\/div>\n\n<\/div><\/div>\n\n\n<div class=\"blockGlobal paddingTopMD paddingBottomLG noRadius\" style=\"background-color:#fff;\">\n\t\t<div class=\"container\">\n        \t\t<div class=\"grid\">\n            \n                        <div class=\"col-md-6 mediaLeft cardInforWidgetWrap\">\n                <div class=\"maincopy\">\n                                            <h2 class=\"h4\">Parking<\/h2>\n                                        \n                                            <p><p>Detroit Metro Airport offers a variety of convenient, economical parking options, including handicap accessible parking spots.\u00a0<a href=\"https:\/\/metroairport.com\/dtw\/other-ground-transportation\/shuttle-services\">Free shuttles<\/a>\u00a0run between on-airport parking lots and the Ground Transportation Center at both terminals.<\/p>\n<p>Concerned about vehicle height restrictions? Most areas of the parking structures cannot accommodate vehicles over 7\u20190\u201d high. However, van accessible parking spaces (for vehicles not exceeding 8\u20192\u201d in height) are available in the following designated areas of the Big Blue Deck and McNamara Parking Garages:<\/p>\n<ul>\n<li>McNamara Deck: Located on Level 2 near the B elevators. Enter the garage from international arrivals and follow the signs to 8\u20192\u201d parking.<\/li>\n<li>Big Blue Deck: Located on Level 1 in zones A and B.<\/li>\n<\/ul>\n<p>For vehicles higher than 8&#8217;2&#8243; in height, parking spaces are available at the Green Lot. View the\u00a0<a href=\"http:\/\/metroairport.com\/sites\/default\/files\/business_documents\/Parking\/McNamara_Clearance_Isometric.pdf\">Height Restrictions at McNamara Parking Garage<\/a>.<\/p>\n<p><strong>NOTE<\/strong>: For customer safety, electric vehicles that have\u00a0<strong>NOT<\/strong>\u00a0been repaired for applicable recalls are\u00a0<strong>PROHIBITED<\/strong>\u00a0from parking at all DTW facilities. Thank you for your compliance.<\/p>\n<\/p>\n                                    <\/div>\n            <\/div>\n            \n                        <div class=\"col-md-6 cardParkingWidgetWrap\">\n                <div class=\"cardWidget\">\n                                    <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481192281\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row\">\n    <div class=\"parking-name\">\n      <p>Big Blue Deck Long Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n<div class=\"parking-row\">\n    <div class=\"parking-name\">\n      <p>Big Blue Deck Short Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n<div class=\"parking-row\">\n    <div class=\"parking-name\">\n      <p>Green Lot 1 (Credit Card Payment Only)<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n<div class=\"parking-row\">\n    <div class=\"parking-name\">\n      <p>McNamara Long Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n<div class=\"parking-row\">\n    <div class=\"parking-name\">\n      <p>McNamara Short Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n<div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>Valet Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--full\">\n      <i class=\"fa-light fa-circle-xmark\"><\/i>\n      Full\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481192281';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                <\/div>\n            <\/div>\n            \n            \n        <\/div>\n        \t<\/div>\n<\/div>\n\n\n\n<div id=\"parkingoptions\" class=\"blockGlobal paddingTopLG paddingBottomLG parkingTablesBlock noRadius\" style=\"background-color:#fff;\">\n\t    <div class=\"container\">\n    \n\n<!--Option 1-->\n<div class=\"grid wysiwyg wysiwygOption1 intro\">\n\t<div class=\"col-sm-8\">\n\n\t\t\t<div class=\"maincopy light\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"h2 \">Explore All Parking Options<\/h2>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\n\t\t\t\n\t\t\t\n\t\t\t\n\t<\/div>\n<\/div>\n\n\n<!--Option 2-->\n\n\n<!--Option 3-->\n\n\n            <div class=\"tabsWrap parkingTabsWrap\" data-parking-instance=\"parking_tab_6a1ffcb71fea1\">\n            <!-- Mobile Dropdown (Hidden on Desktop) -->\n            <div class=\"parkingTabsDropdown showMobile\">\n                \n<div class=\"dropdown-container\">\n    \n  <div class=\"dropdown-wrapper dropdown-parking_tabs_select_parking_tab_6a1ffcb71fea1_dropdown_8304  \">\n\t<select id=\"parking_tabs_select_parking_tab_6a1ffcb71fea1_dropdown_8304\"   aria-label=\"\">\n\t  \t\t<option value=\"parking-tab-0-parking_tab_6a1ffcb71fea1\">Short-Term Parking<\/option>\n\t  \t\t<option value=\"parking-tab-1-parking_tab_6a1ffcb71fea1\">Long-Term Parking<\/option>\n\t  \t\t<option value=\"parking-tab-2-parking_tab_6a1ffcb71fea1\">Valet<\/option>\n\t  \t\t<option value=\"parking-tab-3-parking_tab_6a1ffcb71fea1\">Evans Terminal (Big Blue Deck)<\/option>\n\t  \t\t<option value=\"parking-tab-4-parking_tab_6a1ffcb71fea1\">McNamara Terminal<\/option>\n\t  \t<\/select>\n  <\/div>\n<\/div>\n\n\n<script>\n  const parking_tabs_select_parking_tab_6a1ffcb71fea1_dropdown_8304 = initChoices('#parking_tabs_select_parking_tab_6a1ffcb71fea1_dropdown_8304', {\n  removeItemButton: false,\n  placeholder: true,\n  placeholderValue: '',\n  searchEnabled: false,\n  shouldSort: false,\n  itemSelectText: '',\n});\n<\/script>\n            <\/div>\n\n            <!-- Desktop Tabs (Hidden on Mobile) -->\n            <div class=\"tabWrapper parkingTabWrapper hideMobile\">\n                                    <button\n                        class=\"tabLink active\"\n                        onclick=\"openTabComponent(event, 'parking-tab-0-parking_tab_6a1ffcb71fea1')\"\n                        id=\"openDefault\"                        data-tab-id=\"parking-tab-0-parking_tab_6a1ffcb71fea1\">\n                        Short-Term Parking                    <\/button>\n                                    <button\n                        class=\"tabLink\"\n                        onclick=\"openTabComponent(event, 'parking-tab-1-parking_tab_6a1ffcb71fea1')\"\n                                                data-tab-id=\"parking-tab-1-parking_tab_6a1ffcb71fea1\">\n                        Long-Term Parking                    <\/button>\n                                    <button\n                        class=\"tabLink\"\n                        onclick=\"openTabComponent(event, 'parking-tab-2-parking_tab_6a1ffcb71fea1')\"\n                                                data-tab-id=\"parking-tab-2-parking_tab_6a1ffcb71fea1\">\n                        Valet                    <\/button>\n                                    <button\n                        class=\"tabLink\"\n                        onclick=\"openTabComponent(event, 'parking-tab-3-parking_tab_6a1ffcb71fea1')\"\n                                                data-tab-id=\"parking-tab-3-parking_tab_6a1ffcb71fea1\">\n                        Evans Terminal (Big Blue Deck)                    <\/button>\n                                    <button\n                        class=\"tabLink\"\n                        onclick=\"openTabComponent(event, 'parking-tab-4-parking_tab_6a1ffcb71fea1')\"\n                                                data-tab-id=\"parking-tab-4-parking_tab_6a1ffcb71fea1\">\n                        McNamara Terminal                    <\/button>\n                            <\/div>\n\n            <!-- Tab Content Areas -->\n                            <div\n                    id=\"parking-tab-0-parking_tab_6a1ffcb71fea1\"\n                    data-tab-panel=\"parking-tab-0-parking_tab_6a1ffcb71fea1\"\n                    class=\"tabContent parkingTabContent\"\n                    style=\"display: block;\">\n\n                    <div class=\"grid\">\n                                                    <div class=\"parkingTabIntro col-md-5 mediaLeft\">\n                                                                    <h2 class=\"h4\">Short-Term Parking<\/h2>\n                                                                                                    <div class=\"maincopy\">\n                                        <p>There are convenient short-term parking options available for travelers needing quick, easy access for drop-off\/pick-up near the terminals. Short-term parking is available at the McNamara Deck and Evans Desk (Big Blue Deck).<\/p>\n                                    <\/div>\n                                                            <\/div>\n                        \n                                                    <div class=\"parkingTablesGrid col-md-7 mediaRight\">\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481192636\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>Big Blue Deck Short Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481192636';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-4\" class=\"tablepress tablepress-id-4\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">Big Blue Deck Short-Term<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$43.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">$4.00<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">$6.00<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">$7.00<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">$8.00<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">$24.00<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">$28.00<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">$36.00<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">$40.00<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">$43.00<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-4 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481192872\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>McNamara Short Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481192872';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-3\" class=\"tablepress tablepress-id-3\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">McNamara Short-Term<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$44.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">$4.00<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">$6.00<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">$7.00<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">$8.00<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">$24.00<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">$28.00<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">$36.00<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">$40.00<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">$44.00<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-3 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                            <\/div>\n                                            <\/div>\n                <\/div>\n                            <div\n                    id=\"parking-tab-1-parking_tab_6a1ffcb71fea1\"\n                    data-tab-panel=\"parking-tab-1-parking_tab_6a1ffcb71fea1\"\n                    class=\"tabContent parkingTabContent\"\n                    style=\"display: none;\">\n\n                    <div class=\"grid\">\n                                                    <div class=\"parkingTabIntro col-md-5 mediaLeft\">\n                                                                    <h2 class=\"h4\">Long\u2011Term Parking<\/h2>\n                                                                                                    <div class=\"maincopy\">\n                                        <p>Long term parking is available in the McNamara Parking Garage, the Big Blue Deck and the Green Lots. Vehicles taller than 7\u2019 can park in the Green Lots. All long term parking areas offer handicap accessible parking.<\/p>\n<p>Visitors can call Detroit Metro Airport\u2019s 24-hour parking hotline <a href=\"tel:800-642-1978\">800-642-1978<\/a> for up-to-the-minute parking statuses.<\/p>\n                                    <\/div>\n                                                            <\/div>\n                        \n                                                    <div class=\"parkingTablesGrid col-md-7 mediaRight\">\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481193130\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>Big Blue Deck Long Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481193130';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-6\" class=\"tablepress tablepress-id-6\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">Big Blue Deck Long-Term<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$23.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">$4.00<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">$6.00<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">$7.00<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">$8.00<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">$23.00<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-6 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481193418\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>McNamara Long Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481193418';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-5\" class=\"tablepress tablepress-id-5\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">McNamara Long-Term<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">$4.00<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">$6.00<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">$7.00<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">$8.00<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">$24.00<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">$28.00<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">$36.00<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">$40.00<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-5 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481193855\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>Green Lot 1 (Credit Card Payment Only)<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481193855';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-7\" class=\"tablepress tablepress-id-7\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">Green Lot<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-7 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                            <\/div>\n                                            <\/div>\n                <\/div>\n                            <div\n                    id=\"parking-tab-2-parking_tab_6a1ffcb71fea1\"\n                    data-tab-panel=\"parking-tab-2-parking_tab_6a1ffcb71fea1\"\n                    class=\"tabContent parkingTabContent\"\n                    style=\"display: none;\">\n\n                    <div class=\"grid\">\n                                                    <div class=\"parkingTabIntro col-md-5 mediaLeft\">\n                                                                    <h2 class=\"h4\">Valet<\/h2>\n                                                                                                    <div class=\"maincopy\">\n                                        <p>DTW offers valet parking services at the McNamara Terminal. Upon entering the airport, please follow signage for Departures and Valet Parking. An attendant will take your information, assist with your luggage and provide a ticket for vehicle retrieval. All valet vehicles are securely stored on Level 6 of our monitored parking garage.<\/p>\n<p>Acceptable payments include cash, credit card, Google Pay, Apple Wallet or use Valet Text-N-Go. Save time by texting your valet.<\/p>\n<p>ticket number to:\u00a0<a href=\"tel:734-725-2552\">734.725.2552<\/a>.<\/p>\n                                    <\/div>\n                                                            <\/div>\n                        \n                                                    <div class=\"parkingTablesGrid col-md-7 mediaRight\">\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481194091\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>Valet Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--full\">\n      <i class=\"fa-light fa-circle-xmark\"><\/i>\n      Full\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481194091';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-8\" class=\"tablepress tablepress-id-8\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">McNamara Valet<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$51.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">$24.00<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">$28.00<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">$36.00<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">$40.00<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">$44.00<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">$51.00<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-8 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                            <\/div>\n                                            <\/div>\n                <\/div>\n                            <div\n                    id=\"parking-tab-3-parking_tab_6a1ffcb71fea1\"\n                    data-tab-panel=\"parking-tab-3-parking_tab_6a1ffcb71fea1\"\n                    class=\"tabContent parkingTabContent\"\n                    style=\"display: none;\">\n\n                    <div class=\"grid\">\n                                                    <div class=\"parkingTabIntro col-md-5 mediaLeft\">\n                                                                    <h2 class=\"h4\">Evans Terminal (Big Blue Deck)<\/h2>\n                                                                                                    <div class=\"maincopy\">\n                                        <p>Evans Terminal offers short-term and long-term parking in the Big Blue Deck lot.<\/p>\n                                    <\/div>\n                                                            <\/div>\n                        \n                                                    <div class=\"parkingTablesGrid col-md-7 mediaRight\">\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481192636\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>Big Blue Deck Short Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481192636';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-4-no-2\" class=\"tablepress tablepress-id-4\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">Big Blue Deck Short-Term<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$43.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">$4.00<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">$6.00<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">$7.00<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">$8.00<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">$24.00<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">$28.00<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">$36.00<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">$40.00<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">$43.00<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-4-no-2 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481193130\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>Big Blue Deck Long Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481193130';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-6-no-2\" class=\"tablepress tablepress-id-6\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">Big Blue Deck Long-Term<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$23.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">$4.00<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">$6.00<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">$7.00<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">$8.00<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">$23.00<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-6-no-2 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                            <\/div>\n                                            <\/div>\n                <\/div>\n                            <div\n                    id=\"parking-tab-4-parking_tab_6a1ffcb71fea1\"\n                    data-tab-panel=\"parking-tab-4-parking_tab_6a1ffcb71fea1\"\n                    class=\"tabContent parkingTabContent\"\n                    style=\"display: none;\">\n\n                    <div class=\"grid\">\n                                                    <div class=\"parkingTabIntro col-md-5 mediaLeft\">\n                                                                    <h2 class=\"h4\">McNamara Terminal<\/h2>\n                                                                                                    <div class=\"maincopy\">\n                                        <p>McNamara Terminal offers three parking options: short-term, long-term, and valet parking.<\/p>\n                                    <\/div>\n                                                            <\/div>\n                        \n                                                    <div class=\"parkingTablesGrid col-md-7 mediaRight\">\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481192872\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>McNamara Short Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481192872';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-3-no-2\" class=\"tablepress tablepress-id-3\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">McNamara Short-Term<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$44.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">$4.00<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">$6.00<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">$7.00<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">$8.00<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">$24.00<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">$28.00<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">$36.00<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">$40.00<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">$44.00<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-3-no-2 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481193418\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>McNamara Long Term Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--open\">\n      <i class=\"fa-light fa-circle-check\"><\/i>\n      Open\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481193418';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-5-no-2\" class=\"tablepress tablepress-id-5\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">McNamara Long-Term<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">$4.00<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">$6.00<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">$7.00<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">$8.00<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">$24.00<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">$28.00<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">$36.00<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">$40.00<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-5-no-2 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                                    <div class=\"parkingTableItem beefup openAccordion accordionStyle\">\n\n                                                                                    <div class=\"parkingTableWidget openTitle beefup__head\">\n                                                <span class=\"beefup__head--title\">\n                                                            <h2 class=\"h5\">Parking Status and Availability<\/h2>\n                                                <!-- FlyFruition Widget Dependencies -->\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"anonymous\" \/>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Lato:wght@400;700&#038;family=Roboto:wght@400;500;700&#038;family=Space+Grotesk:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/widget.css\" \/>\n<link rel=\"stylesheet\" href=\"https:\/\/pages.fruitionqa.com\/styles\/airports\/dtw.css\" \/>\n<script>\n(function() {\n  if (!document.querySelector('script[src*=\"kit.fontawesome.com\"]')) {\n    var s = document.createElement('script');\n    s.src = 'https:\/\/kit.fontawesome.com\/f2144e7b52.js';\n    s.crossOrigin = 'anonymous';\n    s.async = true;\n    document.head.appendChild(s);\n  }\n  if (!document.querySelector('[data-typekit-loaded], link[href*=\"typekit.net\"], script[src*=\"typekit.net\"], script[src*=\"use.typekit.net\"]')) {\n    var l = document.createElement('link');\n    l.rel = 'stylesheet';\n    l.href = 'https:\/\/use.typekit.net\/lly7wdy.css';\n    document.head.appendChild(l);\n  }\n})();\n<\/script>\n<style>\n\/* FlyFruition Widget Styles *\/\n.flyfruition-widget {\n  font-family: var(--font-family-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n  color: var(--airport-text-secondary, #3a3e46);\n  line-height: 1.5;\n}\n\n.flyfruition-widget-status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  font-size: 12px;\n  color: var(--airport-text-muted, #7d8796);\n  padding: 4px 8px;\n  background: var(--airport-bg-secondary, #f7fafc);\n  border-radius: 12px;\n  margin-bottom: 12px;\n}\n\n.flyfruition-widget-status__indicator {\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n  animation: flyfruition-pulse 2s infinite;\n}\n\n.flyfruition-widget-status__indicator--connecting {\n  background: #fbbf24;\n}\n\n.flyfruition-widget-status__indicator--connected {\n  background: #22c55e;\n}\n\n.flyfruition-widget-status__indicator--disconnected,\n.flyfruition-widget-status__indicator--error {\n  background: #7d8796;\n  animation: none;\n}\n\n@keyframes flyfruition-pulse {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.5; }\n}\n\n\/* Mini widget specific styles *\/\n.flyfruition-widget--mini .flight-table-wrapper {\n  max-width: 100%;\n}\n\n.flyfruition-widget--mini .filter-bar {\n  display: none;\n}\n\n.flyfruition-widget--mini .flight-table__results-count {\n  display: none;\n}\n\n.flyfruition-widget--mini .load-more-container {\n  display: none;\n}\n<\/style>\n\n<div id=\"flyfruition-parking-DTW-1780481194091\" class=\"flyfruition-widget flyfruition-widget--parking flyfruition-widget--mini\" data-airport=\"DTW\" data-widget=\"parking\">\n  \n  <div class=\"flyfruition-widget__content\">\n    \n<style>\n\/* Minimal Parking Component Styles *\/\n.parking-container {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n}\n\n.parking-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n    padding: 8px;\n    border-bottom: 1px solid var(--color-border-medium, #e6e9eb);\n}\n\n.parking-row-last {\n    border-bottom: none;\n}\n\n.parking-name {\n    display: flex;\n    align-items: center;\n}\n\n.parking-name p {\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-regular, 400);\n    font-size: var(--font-size-base, 15px);\n    line-height: var(--line-height-normal, 1.5);\n    color: var(--color-text-body, #3a3e46);\n    white-space: normal;\n    word-wrap: break-word;\n    margin: 0;\n}\n\n\/* Use higher specificity to override widget.css *\/\n.flyfruition-widget .status-tag,\n.status-tag {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 4px 12px;\n    border: 1px solid;\n    border-radius: 4px;\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n    font-weight: var(--font-weight-medium, 500);\n    font-size: var(--font-size-sm, 13px);\n    line-height: var(--line-height-normal, 1.5);\n    white-space: nowrap;\n}\n\n.flyfruition-widget .status-tag i,\n.status-tag i {\n    font-size: 16px;\n    line-height: 0;\n}\n\n.flyfruition-widget .status-tag--open,\n.status-tag--open {\n    background-color: #d2f1de;\n    border-color: #49c67c;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--open i,\n.status-tag--open i {\n    color: #1cb85b;\n}\n\n.flyfruition-widget .status-tag--full,\n.status-tag--full {\n    background-color: #ffefed;\n    border-color: #f56957;\n    color: #3a3e46;\n}\n\n.flyfruition-widget .status-tag--full i,\n.status-tag--full i {\n    color: #f3442d;\n}\n\n\/* License Component Styles *\/\n.license-wrapper {\n    display: flex;\n    justify-content: flex-end;\n}\n\n.license {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-sm, 8px);\n    padding: var(--spacing-md, 12px);\n    font-family: 'aktiv-grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.license__icon {\n    font-size: 16px;\n    color: var(--color-text-light, #7d8796);\n    flex-shrink: 0;\n}\n\n.license__text {\n    font-size: 13px;\n    line-height: 20px;\n    color: var(--color-text-light, #7d8796);\n    margin: 0;\n}\n\n.license__link {\n    color: #0064A7;\n    text-decoration: none;\n    font-weight: 500;\n    transition: color 0.2s;\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.license__link:hover {\n    color: #004A78;\n    text-decoration: underline;\n}\n\n.license__link-icon {\n    font-size: 11px;\n}\n\n@media (max-width: 767px) {\n    .parking-container {\n        width: 100%;\n    }\n}\n<\/style><div class=\"parking-container\">\n    <div class=\"parking-row parking-row-last\">\n    <div class=\"parking-name\">\n      <p>Valet Parking<\/p>\n    <\/div>\n    <div class=\"status-tag status-tag--full\">\n      <i class=\"fa-light fa-circle-xmark\"><\/i>\n      Full\n    <\/div>\n  <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<!-- FlyFruition Live Update Script -->\n<script>\n(function() {\n  const widgetId = 'flyfruition-parking-DTW-1780481194091';\n  const widgetType = 'parking';\n  const airport = 'DTW';\n  const channel = 'dtw-parking';\n  const wsUrl = 'wss:\/\/dev-api.flyfruition.com\/';\n  const wsKey = '2QicQPH7AOM7ASWXGY4VkRiUlYMhOtW';\n  const refreshUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking?mini=true';\n  const dataUrl = 'https:\/\/pages.fruitionqa.com\/api\/widgets\/dtw\/parking\/data';\n  \n  let ws = null;\n  let reconnectAttempts = 0;\n  const maxReconnectAttempts = 5;\n  const reconnectDelay = 3000;\n  \n  function connect() {\n    if (ws && ws.readyState === WebSocket.OPEN) return;\n    \n    try {\n      ws = new WebSocket(wsUrl + '?publicKey=' + wsKey);\n      \n      ws.onopen = function() {\n        console.log('[FlyFruition Widget] WebSocket connected');\n        reconnectAttempts = 0;\n        \/\/ Join the channel\n        ws.send(JSON.stringify({ action: 'joinchannel', channel: channel }));\n        updateStatus('connected');\n      };\n      \n      ws.onmessage = function(event) {\n        try {\n          const data = JSON.parse(event.data);\n          if (data.channel === channel || data.type === 'update') {\n            console.log('[FlyFruition Widget] Update received, refreshing...');\n            refreshWidget().catch(function(error) {\n              console.error('[FlyFruition Widget] Failed to refresh on WebSocket update:', error);\n            });\n          }\n        } catch (e) {\n          \/\/ Non-JSON message, might be a heartbeat\n        }\n      };\n      \n      ws.onclose = function() {\n        console.log('[FlyFruition Widget] WebSocket disconnected');\n        updateStatus('disconnected');\n        scheduleReconnect();\n      };\n      \n      ws.onerror = function(error) {\n        console.error('[FlyFruition Widget] WebSocket error:', error);\n        updateStatus('error');\n      };\n    } catch (error) {\n      console.error('[FlyFruition Widget] Failed to connect:', error);\n      scheduleReconnect();\n    }\n  }\n  \n  function scheduleReconnect() {\n    if (reconnectAttempts < maxReconnectAttempts) {\n      reconnectAttempts++;\n      console.log('[FlyFruition Widget] Reconnecting in ' + (reconnectDelay \/ 1000) + 's (attempt ' + reconnectAttempts + ')');\n      setTimeout(connect, reconnectDelay);\n    }\n  }\n  \n  function updateStatus(status) {\n    const statusEl = document.querySelector('#' + widgetId + ' .flyfruition-widget-status');\n    if (statusEl) {\n      statusEl.setAttribute('data-status', status);\n      const indicator = statusEl.querySelector('.flyfruition-widget-status__indicator');\n      if (indicator) {\n        indicator.className = 'flyfruition-widget-status__indicator flyfruition-widget-status__indicator--' + status;\n      }\n    }\n  }\n  \n  function refreshWidget() {\n    const container = document.getElementById(widgetId);\n    if (!container) return Promise.reject(new Error('Container not found'));\n\n    \/\/ Try JSON data update first (for widgets with implemented DOM update logic)\n    if (hasJsonUpdateSupport(widgetType)) {\n      console.log('[FlyFruition Widget] Fetching fresh JSON data from:', dataUrl);\n      return fetch(dataUrl)\n        .then(function(response) {\n          if (!response.ok) throw new Error('Failed to fetch JSON data');\n          return response.json();\n        })\n        .then(function(data) {\n          console.log('[FlyFruition Widget] Fresh JSON data received:', data);\n          updateWidgetWithData(container, data, widgetType, airport);\n        })\n        .catch(function(error) {\n          console.error('[FlyFruition Widget] Failed to fetch JSON data, falling back to HTML:', error);\n          return refreshWidgetWithHtml(container);\n        });\n    } else {\n      \/\/ Fall back to HTML replacement for widgets without JSON update logic\n      return refreshWidgetWithHtml(container);\n    }\n  }\n\n  function hasJsonUpdateSupport(type) {\n    \/\/ Widgets with fully implemented DOM update logic using shared rendering utilities\n    return ['weather', 'security', 'parking', 'tsa-cards', 'dine-shop-relax'].indexOf(type) !== -1;\n  }\n\n  function refreshWidgetWithHtml(container) {\n    console.log('[FlyFruition Widget] Fetching fresh HTML from:', refreshUrl);\n    return fetch(refreshUrl)\n      .then(function(response) {\n        if (!response.ok) throw new Error('Failed to fetch HTML');\n        return response.text();\n      })\n      .then(function(html) {\n        console.log('[FlyFruition Widget] Fresh HTML received, updating widget');\n        \/\/ Extract just the widget content (not dependencies)\n        var parser = new DOMParser();\n        var doc = parser.parseFromString(html, 'text\/html');\n        var newContent = doc.querySelector('.flyfruition-widget__content');\n        var currentContent = container.querySelector('.flyfruition-widget__content');\n        if (newContent &#038;&#038; currentContent) {\n          currentContent.innerHTML = newContent.innerHTML;\n        }\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to refresh with HTML:', error);\n        throw error;\n      });\n  }\n\n  function updateWidgetWithData(container, data, type, airportCode) {\n    \/\/ Widget-specific JSON-to-DOM update logic\n    switch(type) {\n      case 'weather':\n        updateWeatherWidget(container, data);\n        break;\n      case 'security':\n        updateSecurityWidget(container, data);\n        break;\n      case 'parking':\n        updateParkingWidget(container, data);\n        break;\n      case 'tsa-cards':\n        updateTSACardsWidget(container, data);\n        break;\n      case 'dine-shop-relax':\n        updateDineShopRelaxWidget(container, data);\n        break;\n      default:\n        console.warn('[FlyFruition Widget] No JSON update logic for widget type:', type);\n    }\n  }\n\n  function updateWeatherWidget(container, data) {\n    var weatherEl = container.querySelector('.weather');\n    if (!weatherEl || !data) return;\n\n    var tempF = Math.round(data.temp_f);\n    var tempC = Math.round(data.temp_c);\n    var weather = data.weather;\n    var icon = getWeatherIcon(weather);\n\n    \/\/ Clear existing content\n    weatherEl.innerHTML = '';\n\n    \/\/ Create icon span safely\n    var iconSpan = document.createElement('span');\n    iconSpan.className = icon;\n    weatherEl.appendChild(iconSpan);\n\n    \/\/ Create text node to prevent XSS\n    var textNode = document.createTextNode(tempF + '\u00b0F \/ ' + tempC + '\u00b0C \/ ' + weather);\n    weatherEl.appendChild(textNode);\n  }\n\n  function getWeatherIcon(weather) {\n    var w = (weather || '').toLowerCase();\n    if (w.includes('clear') || w.includes('sunny')) return 'fa-solid fa-sun';\n    if (w.includes('cloud')) return 'fa-solid fa-clouds';\n    if (w.includes('rain')) return 'fa-solid fa-cloud-rain';\n    if (w.includes('snow')) return 'fa-solid fa-snowflake';\n    if (w.includes('storm') || w.includes('thunder')) return 'fa-solid fa-cloud-bolt';\n    if (w.includes('fog') || w.includes('mist')) return 'fa-solid fa-smog';\n    return 'fa-solid fa-cloud-sun';\n  }\n\n  \/\/ Security Widget Update\n  function updateSecurityWidget(container, data) {\n    var tsaContainer = container.querySelector('.tsa-container');\n    if (!tsaContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderMinimalSecurityWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      tsaContainer.parentElement.replaceChild(newContainer, tsaContainer);\n    }\n  }\n\n  function renderMinimalSecurityWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-container\"><div class=\"tsa-row tsa-row-last\"><div class=\"tsa-name\"><p>No TSA checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderMinimalSecurityRow(checkpoint, index === checkpoints.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"tsa-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalSecurityRow(checkpoint, isLast) {\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var statusClass = lowestWaitTime ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = lowestWaitTime ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = lowestWaitTime || 'Closed';\n    var rowClass = isLast ? 'tsa-row tsa-row-last' : 'tsa-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"tsa-name\"><p>' + checkpoint.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getLowestWaitTime(checkpoint) {\n    if (!checkpoint.lanes || checkpoint.lanes.length === 0) return null;\n    var times = checkpoint.lanes.filter(function(lane) { return lane.wait_time; }).map(function(lane) { return lane.wait_time; });\n    return times.length > 0 ? times.sort()[0] : null;\n  }\n\n  \/\/ Parking Widget Update\n  function updateParkingWidget(container, data) {\n    var parkingContainer = container.querySelector('.parking-container');\n    if (!parkingContainer || !data || !data.parkingLots) return;\n\n    var newHtml = renderMinimalParkingWidget(data.parkingLots);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      parkingContainer.parentElement.replaceChild(newContainer, parkingContainer);\n    }\n  }\n\n  function renderMinimalParkingWidget(lots) {\n    if (lots.length === 0) {\n      return '<div class=\"parking-container\"><div class=\"parking-row parking-row-last\"><div class=\"parking-name\"><p>No parking information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var rowsHtml = lots.map(function(lot, index) {\n      return renderMinimalParkingRow(lot, index === lots.length - 1);\n    }).join('\\n');\n\n    return '<div class=\"parking-container\">' + rowsHtml + '<\/div>';\n  }\n\n  function renderMinimalParkingRow(lot, isLast) {\n    var parkingStatus = getParkingStatus(lot.status);\n    var isOpen = parkingStatus === 'open' || parkingStatus === 'limited';\n    var statusClass = isOpen ? 'status-tag--open' : 'status-tag--closed';\n    var statusIcon = isOpen ? 'fa-circle-check' : 'fa-circle-xmark';\n    var statusText = isOpen ? 'Open' : 'Closed';\n    var rowClass = isLast ? 'parking-row parking-row-last' : 'parking-row';\n\n    return '<div class=\"' + rowClass + '\"><div class=\"parking-name\"><p>' + lot.title + '<\/p><\/div><div class=\"status-tag ' + statusClass + '\"><i class=\"fa-light ' + statusIcon + '\"><\/i>' + statusText + '<\/div><\/div>';\n  }\n\n  function getParkingStatus(status) {\n    if (!status) return 'unknown';\n    var s = status.toLowerCase();\n    if (s.includes('open')) return 'open';\n    if (s.includes('closed')) return 'closed';\n    if (s.includes('limited') || s.includes('full')) return 'limited';\n    return 'unknown';\n  }\n\n  \/\/ TSA Cards Widget Update\n  function updateTSACardsWidget(container, data) {\n    var cardsContainer = container.querySelector('.tsa-cards-container');\n    if (!cardsContainer || !data || !data.checkpoints) return;\n\n    var newHtml = renderTSACardsWidget(data.checkpoints);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newContainer = temp.firstElementChild;\n\n    if (newContainer) {\n      cardsContainer.parentElement.replaceChild(newContainer, cardsContainer);\n    }\n  }\n\n  function renderTSACardsWidget(checkpoints) {\n    if (checkpoints.length === 0) {\n      return '<div class=\"tsa-cards-container\"><div class=\"tsa-card tsa-card--primary\"><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">No checkpoint information available.<\/p><\/div><\/div><\/div>';\n    }\n\n    var cardsHtml = checkpoints.map(function(checkpoint, index) {\n      return renderTSACard(checkpoint, index);\n    }).join('\\n    ');\n\n    return '<div class=\"tsa-cards-container\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderTSACard(checkpoint, index) {\n    var variant = getCardVariant(index);\n    var lowestWaitTime = getLowestWaitTime(checkpoint);\n    var gatesInfo = extractGatesInfo(checkpoint);\n    var infoList = getCheckpointInfoList(checkpoint);\n    var displayWaitTime = lowestWaitTime || 'N\/A';\n\n    var gatesHtml = gatesInfo ? '<p class=\"tsa-card__gates\">' + gatesInfo + '<\/p>' : '';\n    var infoListHtml = infoList.length > 0 ? '<ul class=\"tsa-card__info-list\">' + infoList.map(function(item) { return '<li>' + item + '<\/li>'; }).join('\\n        ') + '<\/ul>' : '';\n\n    return '<div class=\"tsa-card tsa-card--' + variant + '\"><div class=\"tsa-card__header tsa-card__header--' + variant + '\"><h2 class=\"tsa-card__terminal\">' + checkpoint.title + '<\/h2>' + gatesHtml + '<\/div><div class=\"tsa-card__body\"><p class=\"tsa-card__wait-time\">' + displayWaitTime + '<\/p>' + infoListHtml + '<\/div><\/div>';\n  }\n\n  function getCardVariant(index) {\n    var variants = ['primary', 'secondary', 'tertiary'];\n    return variants[index % 3];\n  }\n\n  function extractGatesInfo(checkpoint) {\n    var searchText = (checkpoint.location || '') + ' ' + (checkpoint.description || '');\n    var gateMatch = searchText.match(\/Gates?\\s+[\\d\\-,\\s]+\/i);\n    return gateMatch ? gateMatch[0] : null;\n  }\n\n  function getCheckpointInfoList(checkpoint) {\n    var infoItems = [];\n    var openingInfo = checkpoint.lanes.find(function(lane) { return lane.opening_info; });\n    if (openingInfo) infoItems.push(openingInfo.opening_info);\n\n    var hasPreCheck = checkpoint.lanes.some(function(lane) {\n      var title = lane.title.toLowerCase();\n      return title.includes('precheck') || title.includes('pre-check') || title.includes('pre\u2713');\n    });\n    if (hasPreCheck) infoItems.push('TSA PreCheck');\n\n    var hasClear = checkpoint.lanes.some(function(lane) {\n      return lane.title.toLowerCase().includes('clear');\n    }) || (checkpoint.description && checkpoint.description.toLowerCase().includes('clear'));\n    if (hasClear) infoItems.push('CLEAR+');\n\n    return infoItems;\n  }\n\n  \/\/ Dine Shop Relax Widget Update\n  function updateDineShopRelaxWidget(container, data) {\n    var gridEl = container.querySelector('.amenities-grid');\n    if (!gridEl || !data || !data.amenities) return;\n\n    var newHtml = renderAmenitiesGrid(data.amenities);\n    var temp = document.createElement('div');\n    temp.innerHTML = newHtml;\n    var newGridOrEmpty = temp.firstElementChild;\n\n    if (!newGridOrEmpty) return;\n    gridEl.parentElement.replaceChild(newGridOrEmpty, gridEl);\n\n    \/\/ Trigger event to notify filter script that grid was updated\n    var event = new CustomEvent('flyfruition:grid-updated', { bubbles: true });\n    container.dispatchEvent(event);\n  }\n\n  \/\/ Helper functions for time formatting\n  function convertTo12Hour(time) {\n    var match = time.match(\/(\\d{1,2}):(\\d{2})\/);\n    if (!match) return time;\n\n    var hours = parseInt(match[1], 10);\n    var minutes = match[2];\n    var ampm = hours >= 12 ? 'pm' : 'am';\n\n    if (hours === 0) {\n      hours = 12; \/\/ Midnight\n    } else if (hours > 12) {\n      hours -= 12; \/\/ Convert to 12-hour\n    }\n\n    return hours + ':' + minutes + ampm;\n  }\n\n  function formatOperationHours(hours) {\n    if (!hours) return '';\n    \/\/ Replace all time patterns (HH:MM) with 12-hour format\n    return hours.replace(\/(\\d{1,2}):(\\d{2})\/g, function(match) {\n      return convertTo12Hour(match);\n    });\n  }\n\n  function renderAmenitiesGrid(items) {\n    if (items.length === 0) {\n      return '<div class=\"amenities-grid__empty\"><i class=\"fa-light fa-magnifying-glass\" aria-hidden=\"true\"><\/i><p class=\"amenities-grid__empty-title\">No results found<\/p><p class=\"amenities-grid__empty-text\">Try adjusting your search or filters<\/p><\/div>';\n    }\n\n    \/\/ Sort items by category (Dine, Shop, Relax) then alphabetically within each category\n    var categoryOrder = { dine: 0, shop: 1, relax: 2 };\n    var sortedItems = items.slice().sort(function(a, b) {\n      var categoryA = getAmenityCategory(a.type);\n      var categoryB = getAmenityCategory(b.type);\n\n      \/\/ First sort by category\n      var categoryDiff = categoryOrder[categoryA] - categoryOrder[categoryB];\n      if (categoryDiff !== 0) {\n        return categoryDiff;\n      }\n\n      \/\/ Within same category, sort alphabetically by name\n      return a.name.localeCompare(b.name);\n    });\n\n    var cardsHtml = sortedItems.map(function(item) {\n      return renderAmenityCard(item);\n    }).join('\\n');\n\n    return '<div class=\"amenities-grid\" role=\"region\" aria-label=\"Airport amenities\">' + cardsHtml + '<\/div>';\n  }\n\n  function renderAmenityCard(item) {\n    var category = getAmenityCategory(item.type);\n    var imageUrl = item.thumbnail || (item.images && item.images[0]) || getPlaceholderImage(category);\n    var location = (item.coordinates && item.coordinates.structureName) || 'Airport';\n    var mapUrl = 'https:\/\/maps.metroairport.com\/?poiId=' + item.sourceId;\n    var altText = item.name + ' ' + (category === 'dine' ? 'dining location' : category === 'shop' ? 'retail location' : 'relaxation area');\n    var description = item.description || '';\n\n    \/\/ Prepare search text and offer status for filtering\n    var searchText = (item.description || '').replace(\/\"\/g, '&quot;');\n    var hasOffer = item.metadata && item.metadata.dealTitle && item.metadata.dealTitle.trim() ? 'true' : 'false';\n\n    \/\/ Render info tags (location with airplane icon and hours without background)\n    var infoTags = [];\n    if (item.nearbyLandmark) {\n      infoTags.push('<div class=\"amenity-card__info-tag--location\"><i class=\"fa-light fa-plane-departure\" aria-hidden=\"true\"><\/i><span>' + item.nearbyLandmark + '<\/span><\/div>');\n    }\n    if (item.operationHours) {\n      infoTags.push('<div class=\"amenity-card__info-tag--hours\"><i class=\"fa-light fa-clock\" aria-hidden=\"true\"><\/i><span>' + formatOperationHours(item.operationHours) + '<\/span><\/div>');\n    }\n    var infoTagsHtml = infoTags.length > 0 ? '<div class=\"amenity-card__info-tags\">' + infoTags.join('') + '<\/div>' : '';\n\n    \/\/ Truncate description if longer than 150 characters\n    var maxDescriptionLength = 150;\n    var needsTruncation = description && description.length > maxDescriptionLength;\n    var truncatedDescription = needsTruncation ? description.substring(0, maxDescriptionLength) + '...' : description;\n\n    var tagHtml = renderAmenityTag(item.type);\n    var descHtml = description ? '<p class=\"amenity-card__description\" data-full-text=\"' + description.replace(\/\"\/g, '&quot;') + '\" data-truncated-text=\"' + truncatedDescription.replace(\/\"\/g, '&quot;') + '\">' + truncatedDescription + '<\/p>' : '';\n    var toggleHtml = needsTruncation ? '<button class=\"amenity-card__toggle-description\" aria-expanded=\"false\"><span class=\"amenity-card__toggle-text\">Show full description<\/span><i class=\"fa-light fa-circle-plus\" aria-hidden=\"true\"><\/i><\/button>' : '';\n\n    return '<article class=\"amenity-card\" data-category=\"' + category + '\" data-location=\"' + location + '\" data-search-text=\"' + searchText + '\" data-has-offer=\"' + hasOffer + '\"><div class=\"amenity-card__image-wrapper\"><img decoding=\"async\" src=\"' + imageUrl + '\" alt=\"' + altText + '\" class=\"amenity-card__image\" loading=\"lazy\">' + tagHtml + '<\/div><div class=\"amenity-card__content\">' + infoTagsHtml + '<div class=\"amenity-card__body\"><h3 class=\"amenity-card__title\">' + item.name + '<\/h3>' + descHtml + toggleHtml + '<\/div><a href=\"' + mapUrl + '\" class=\"amenity-card__map-btn\" target=\"_blank\" rel=\"noopener noreferrer\" aria-label=\"Open interactive map for ' + item.name + ' (opens in new window)\" title=\"Open interactive map for ' + item.name + '\"><i class=\"fa-light fa-location-dot\"><\/i>Open Interactive Map<\/a><\/div><\/article>';\n  }\n\n  function renderAmenityTag(type) {\n    var category = getAmenityCategory(type);\n    var label = getAmenityCategoryLabel(category);\n    var icon = getAmenityCategoryIcon(category);\n    var cssClass = getAmenityCategoryClass(category);\n    return '<span class=\"amenity-card__tag ' + cssClass + '\" aria-label=\"Category: ' + label + '\"><i class=\"fa-light ' + icon + '\" aria-hidden=\"true\"><\/i>' + label + '<\/span>';\n  }\n\n  function getAmenityCategory(type) {\n    if (!type) return 'relax';\n    var t = type.toLowerCase();\n    if (t.includes('restaurant') || t.includes('dining') || t.includes('food')) return 'dine';\n    if (t.includes('shop') || t.includes('retail') || t.includes('store')) return 'shop';\n    return 'relax';\n  }\n\n  function getAmenityCategoryLabel(category) {\n    return category === 'dine' ? 'Dine' : category === 'shop' ? 'Shop' : 'Relax';\n  }\n\n  function getAmenityCategoryIcon(category) {\n    return category === 'dine' ? 'fa-utensils' : category === 'shop' ? 'fa-bag-shopping' : 'fa-couch';\n  }\n\n  function getAmenityCategoryClass(category) {\n    return 'amenity-card__tag--' + category;\n  }\n\n  function getPlaceholderImage(category) {\n    return category === 'dine' ? '\/images\/placeholder-dine.jpg' : category === 'shop' ? '\/images\/placeholder-shop.jpg' : '\/images\/placeholder-relax.jpg';\n  }\n\n  function initialize() {\n    console.log('[FlyFruition Widget] Fetching fresh data before connecting...');\n    \/\/ First, refresh the widget with fresh data from the endpoint\n    refreshWidget()\n      .then(function() {\n        console.log('[FlyFruition Widget] Fresh data loaded, now connecting to WebSocket...');\n        \/\/ Then connect to WebSocket for live updates\n        connect();\n      })\n      .catch(function(error) {\n        console.error('[FlyFruition Widget] Failed to fetch initial data, connecting to WebSocket anyway...');\n        \/\/ Even if initial fetch fails, still connect to WebSocket\n        connect();\n      });\n  }\n\n  \/\/ Initialize when DOM is ready\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initialize);\n  } else {\n    initialize();\n  }\n  \n  \/\/ Cleanup on page unload\n  window.addEventListener('beforeunload', function() {\n    if (ws) {\n      ws.close();\n    }\n  });\n})();\n<\/script>                \n                \n<a class=\"links medium light \" target=\"_self\" rel=\"noopener noreferrer\"  href=\"\/parking-and-transportation\/parking\/\">\n\n\t\n\t<span class=\"linksTitle\">View all Parking Info<\/span>\n    \n\t\t<span class=\"fa-light fa-arrow-right iconRight\"><\/span>\n\t<\/a>\n<div class=\"license-wrapper-theme\">\n      <div class=\"license\">\n        <p class=\"license__text\">\n          Powered by\n          <a href=\"https:\/\/flyfruition.com\/\" class=\"license__link\" target=\"_blank\" rel=\"noopener noreferrer\">\n            FlyFruition\n            <i class=\"fa-light fa-arrow-up-right-from-square license__link-icon\" aria-hidden=\"true\"><\/i>\n          <\/a>\n        <\/p>\n      <\/div>\n    <\/div>                                                <\/span>\n                                            <\/div>\n\n                                        \n                                                                                    <div class=\"parkingTableCopyWrap beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n                                            \n                                                                                            <div class=\"parkingTablePress\">\n                                                    \n<table id=\"tablepress-8-no-2\" class=\"tablepress tablepress-id-8\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Time Range<\/th><th class=\"column-2\">McNamara Valet<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">Flat 24-Hour Rate<\/td><td class=\"column-2\">$51.00<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">1\/2 hour or less<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">1\/2 hour to 1 hour<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">1 hour to 1 1\/2 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">1 1\/2 hours to 2 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">2 hours to 3 hours<\/td><td class=\"column-2\">$12.00<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">3 hours to 4 hours<\/td><td class=\"column-2\">$16.00<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">4 hours to 5 hours<\/td><td class=\"column-2\">$20.00<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">5 hours to 6 hours<\/td><td class=\"column-2\">$24.00<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">6 hours to 7 hours<\/td><td class=\"column-2\">$28.00<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">7 hours to 8 hours<\/td><td class=\"column-2\">$32.00<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">8 hours to 9 hours<\/td><td class=\"column-2\">$36.00<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">9 hours to 10 hours<\/td><td class=\"column-2\">$40.00<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">10 hours to 11 hours<\/td><td class=\"column-2\">$44.00<\/td>\n<\/tr>\n<tr class=\"row-16\">\n\t<td class=\"column-1\">10 hours to 24 hours<\/td><td class=\"column-2\">&#8211;<\/td>\n<\/tr>\n<tr class=\"row-17\">\n\t<td class=\"column-1\">11 hours to 24 hours<\/td><td class=\"column-2\">$51.00<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-8-no-2 from cache -->                                                <\/div>\n                                                                                        <\/div>\n                                                                            <\/div>\n                                                            <\/div>\n                                            <\/div>\n                <\/div>\n                    <\/div>\n\n        <script type=\"text\/javascript\">\n            \/* <![CDATA[ *\/\n            (function() {\n                'use strict';\n                var instanceId = 'parking_tab_6a1ffcb71fea1';\n\n                function initParkingTabDropdown() {\n                    var dropdownContainer = document.querySelector('.parkingTabsWrap[data-parking-instance=\"' + instanceId + '\"]');\n                    if (!dropdownContainer) return;\n\n                    var selectElement = dropdownContainer.querySelector('.parkingTabsDropdown select');\n                    if (!selectElement) return;\n\n                    var selectId = selectElement.id;\n\n                    \/\/ Set the first option as selected by default\n                    if (selectElement.options.length > 0) {\n                        var firstValue = selectElement.options[0].value;\n                        selectElement.value = firstValue;\n\n                        \/\/ Update Choices.js if it exists\n                        if (typeof window[selectId] !== 'undefined' && window[selectId]) {\n                            try {\n                                window[selectId].setChoiceByValue(firstValue);\n                            } catch (e) {\n                                \/\/ Choices.js not ready yet\n                            }\n                        }\n                    }\n\n                    \/\/ Add change listener to the native select element\n                    selectElement.addEventListener('change', function(e) {\n                        var selectedTabId = e.target.value;\n\n                        \/\/ Hide all parking tab contents within this block\n                        var allTabs = dropdownContainer.querySelectorAll('.parkingTabContent');\n                        allTabs.forEach(function(content) {\n                            content.style.display = 'none';\n                        });\n\n                        \/\/ Show selected tab content\n                        var selectedContent = document.getElementById(selectedTabId);\n                        if (selectedContent) {\n                            selectedContent.style.display = 'block';\n                        }\n                    });\n\n                    \/\/ Listen to Choices.js directly\n                    setTimeout(function() {\n                        if (typeof window[selectId] !== 'undefined' && window[selectId]) {\n                            var choicesElement = selectElement.parentElement.querySelector('.choices');\n                            if (choicesElement) {\n                                choicesElement.addEventListener('change', function(e) {\n                                    selectElement.dispatchEvent(new Event('change'));\n                                });\n                            }\n                        }\n                    }, 100);\n                }\n\n                \/\/ Initialize when DOM is ready\n                if (document.readyState === 'loading') {\n                    document.addEventListener('DOMContentLoaded', function() {\n                        setTimeout(initParkingTabDropdown, 1000);\n                    });\n                } else {\n                    setTimeout(initParkingTabDropdown, 1000);\n                }\n            })();\n            \/* ]]> *\/\n        <\/script>\n    <\/div>\n<\/div>\n\n<div class=\"blockGlobal blueMediumBlueMediumLight paddingTopLG paddingBottomLG cardImageFluid cardsLightText noRadius\">\n\t<img decoding=\"async\" src=\"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/themes\/wcaa\/images\/GraphicSwoop.svg\" class=\"graphicSwoopSingle\" alt=\"\">\t<div class=\"container\">\n\t\t<div class=\"cardImagelWrap dark\">\n\t\t\t\n\n<!--Option 1-->\n<div class=\"grid wysiwyg wysiwygOption1 intro\">\n\t<div class=\"col-sm-8\">\n\n\t\t\t<div class=\"maincopy dark\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"h2 titleWhite\">Electric Vehicle Charging Stations<\/h2>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<p>Charge your Electric Vehicle (EV) for free in the long-term parking areas of the McNamara Deck and Big Blue Deck. Additional charging stations are located at the South Cell Phone lot. Check real-time availability on ChargePoint before you arrive. For customer safety, electric vehicles with unresolved recalls are not permitted in DTW parking facilities.<\/p>\n\t\t\t\t\t\t\t<\/div>\n\n\t\t\t\t\t\t\t<a\nhref=\"https:\/\/driver.chargepoint.com\/mapBounds\/42.25363115000001\/-83.32889394999998\/42.17504015\/-83.37515455000003\/?view=list\"\nclass=\"primaryButton dark\"\ntitle=\"View Availability on Chargepoint\" target=\"_blank\" rel=\"noopener noreferrer\">\nView Availability on Chargepoint<span class=\"fa-light fa-arrow-up-right-from-square\"><\/span>\n<\/a>\n\t\t\t\n\t\t\t\n\t\t\t\n\t<\/div>\n<\/div>\n\n\n<!--Option 2-->\n\n\n<!--Option 3-->\n\n\n\t\t\t\t\t\t\t<div class=\"grid\">\n\t\t\t\n\t\t\t\t\t\t\t\t<div class=\"col-grid col-md-4 col-sm-6 \">\n\t\t\t\t\t\t\n<div class=\"cardImage ghost\">\n\t<div class=\"imageZoomWrap\" style=\"aspect-ratio: 3 \/ 2;\">\n<div class=\"primaryImageWrap\">\n\n\t\n\t\n\t<img decoding=\"async\" src=\"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/parking-and-transportation-01-1-scaled.webp\" \n\t\talt=\"\" \n\t\tclass=\"aspectRatio radiusImage primaryImage\" \n\t\tstyle=\"aspect-ratio: 3 \/ 2;\" \/>\n\n\t\n\t\n\t\n<\/div>\n\n\n<!-- This style is only needed for the first example with aspectRatio -->\n<style>\n\t.aspectRatio:not([style*=\"aspect-ratio\"]) {\n\t\taspect-ratio: 21 \/ 5;\n\t}\n<\/style>\n<\/div>            \n\t<div class=\"cardImageBody\">\n\t\t<h2 class=\"h4\">Big Blue Deck<\/h2>\n\t\t<p><strong>Eight parking spots are located on Level 4.<\/strong><\/p>\n<p>Enter the parking structure through the Long Term parking lanes and follow the green EV parking signs to level 4, then turn right.<\/p>\n\t\t\t<\/div>\n\n\t\t\n\t<\/div>\n\n\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t<div class=\"col-grid col-md-4 col-sm-6 \">\n\t\t\t\t\t\t\n<div class=\"cardImage ghost\">\n\t<div class=\"imageZoomWrap\" style=\"aspect-ratio: 3 \/ 2;\">\n<div class=\"primaryImageWrap\">\n\n\t\n\t\n\t<img decoding=\"async\" src=\"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/parking-and-transportation-10.webp\" \n\t\talt=\"\" \n\t\tclass=\"aspectRatio radiusImage primaryImage\" \n\t\tstyle=\"aspect-ratio: 3 \/ 2;\" \/>\n\n\t\n\t\n\t\n<\/div>\n\n\n<!-- This style is only needed for the first example with aspectRatio -->\n<style>\n\t.aspectRatio:not([style*=\"aspect-ratio\"]) {\n\t\taspect-ratio: 21 \/ 5;\n\t}\n<\/style>\n<\/div>            \n\t<div class=\"cardImageBody\">\n\t\t<h2 class=\"h4\">McNamara Deck<\/h2>\n\t\t<p><strong>Eight EV spots are on Level 8, best accessed from the departure level via the Long Term entrance. <\/strong><\/p>\n<p>Follow green EV signs after the gate, then turn left. Two additional spots are available in valet parking.<\/p>\n\t\t\t<\/div>\n\n\t\t\n\t<\/div>\n\n\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t<div class=\"col-grid col-md-4 col-sm-6 \">\n\t\t\t\t\t\t\n<div class=\"cardImage ghost\">\n\t<div class=\"imageZoomWrap\" style=\"aspect-ratio: 3 \/ 2;\">\n<div class=\"primaryImageWrap\">\n\n\t\n\t\n\t<img decoding=\"async\" src=\"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/parking-and-transportation-11.webp\" \n\t\talt=\"\" \n\t\tclass=\"aspectRatio radiusImage primaryImage\" \n\t\tstyle=\"aspect-ratio: 3 \/ 2;\" \/>\n\n\t\n\t\n\t\n<\/div>\n\n\n<!-- This style is only needed for the first example with aspectRatio -->\n<style>\n\t.aspectRatio:not([style*=\"aspect-ratio\"]) {\n\t\taspect-ratio: 21 \/ 5;\n\t}\n<\/style>\n<\/div>            \n\t<div class=\"cardImageBody\">\n\t\t<h2 class=\"h4\">South Cell Phone Lot<\/h2>\n\t\t<p>Two\u00a0parking spots are located at the South Cell Phone Lot located on eastbound Eureka Road, west of John Dingell Drive.<\/p>\n\t\t\t<\/div>\n\n\t\t\n\t<\/div>\n\n\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\n\t\t\t\n\t\t\t\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n\n<div id=\"parkingpolicies\" class=\"blockGlobal paddingTopLG paddingBottomLG cardIconHorizontalBlock hideIcons noRadius\" style=\"background-color:#D3E7F5;\">\n\t\t<div class=\"container\">\n\t\t\t\t\t<!-- Copy Left Layout -->\n\t\t\t<div class=\"grid\">\n\t\t\t\t<div class=\"col-md-6 mediaLeft\">\n\t\t\t\t\t\t\t\t\t\t\t\n\n<div class=\"wysiwyg\">\n\t<div class=\"maincopy light\">\n\n\t\t\n\t\t\t\t\t\t\t\t\t<h2 class=\"h2 \">Parking Tips<\/h2>\n\t\t\t\t\t\t\t\t\t\t<p><strong>Important details to know before you arrive:<\/strong><\/p>\n<ul>\n<li>Online Parking Reservations Not Currently Offered<\/li>\n<li>All Parking Lots Have Cashless Payment Options<\/li>\n<li>Long-Term Parking Shuttles Run Every 10 Minutes<\/li>\n<li>Price Rates Are Posted at Entrances<\/li>\n<li>Parking Desks Have 7-Foot Clearances<\/li>\n<li>Oversized Vehicle Parking Is Not Available<\/li>\n<li>Maximum Daily Rate Applied for Lost Parking Tickets<\/li>\n<\/ul>\n\t\t\t<\/div>\n\n\t\n\t\n\t\n\t<\/div>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"col-md-6 mediaRight\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n<a class=\"cardHorizontalLink\" href=\"https:\/\/maps.app.goo.gl\/nLUticDLxy4kZY7R6\" target=\"_self\" aria-label=\"Directions to McNamara Terminal Deck\">\n\n<div class=\"cardIconHorizontal boxed cardShadow\">\n\t<div>\n\t\t\n<div class=\"iconWrapper iconGhost medium\">\n\t<span class=\"fa-solid fa-pencil\"><\/span>\n<\/div>\n\t<\/div>\n\t<div>\n\t\t<h5>Directions to McNamara Terminal Deck<\/h5>\t\t\t<\/div>\n<\/div>\n<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n<a class=\"cardHorizontalLink\" href=\"https:\/\/maps.app.goo.gl\/a2qKQ1CarZ93WiNVA\" target=\"_self\" aria-label=\"Directions to Big Blue Deck\">\n\n<div class=\"cardIconHorizontal boxed cardShadow\">\n\t<div>\n\t\t\n<div class=\"iconWrapper iconGhost medium\">\n\t<span class=\"fa-solid fa-pencil\"><\/span>\n<\/div>\n\t<\/div>\n\t<div>\n\t\t<h5>Directions to Big Blue Deck<\/h5>\t\t\t<\/div>\n<\/div>\n<\/a>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n<a class=\"cardHorizontalLink\" href=\"https:\/\/maps.app.goo.gl\/tNigLaFg2fCgX8GX9\" target=\"_self\" aria-label=\"Directions to Green Lot\">\n\n<div class=\"cardIconHorizontal boxed cardShadow\">\n\t<div>\n\t\t\n<div class=\"iconWrapper iconGhost medium\">\n\t<span class=\"fa-solid fa-pencil\"><\/span>\n<\/div>\n\t<\/div>\n\t<div>\n\t\t<h5>Directions to Green Lot<\/h5>\t\t\t<\/div>\n<\/div>\n<\/a>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t\t<\/div>\n<\/div>\n\n<div id=\"parkingreceiptrequestform\" class=\"blockGlobal paddingTopLG paddingBottomLG\" style=\"background-color:#fff;\">\n\t\t<div class=\"container\">\n\n\t\t\t\t<!-- Center layout -->\n\t\t<div class=\"grid\">\n\t\t\t<div class=\"col-md-10 columnCenter\">\n\t\t\t\t\n<div class=\"formWrapper boxed align-center\" style=\"background-color: #fff;\">\n\t\t<h2 class=\"h4\">Parking Receipt Request Form<\/h2>\n\t\t<script type=\"text\/javascript\">\n\/* <![CDATA[ *\/\nvar gform;gform||(document.addEventListener(\"gform_main_scripts_loaded\",function(){gform.scriptsLoaded=!0}),document.addEventListener(\"gform\/theme\/scripts_loaded\",function(){gform.themeScriptsLoaded=!0}),window.addEventListener(\"DOMContentLoaded\",function(){gform.domLoaded=!0}),gform={domLoaded:!1,scriptsLoaded:!1,themeScriptsLoaded:!1,isFormEditor:()=>\"function\"==typeof InitializeEditor,callIfLoaded:function(o){return!(!gform.domLoaded||!gform.scriptsLoaded||!gform.themeScriptsLoaded&&!gform.isFormEditor()||(gform.isFormEditor()&&console.warn(\"The use of gform.initializeOnLoaded() is deprecated in the form editor context and will be removed in Gravity Forms 3.1.\"),o(),0))},initializeOnLoaded:function(o){gform.callIfLoaded(o)||(document.addEventListener(\"gform_main_scripts_loaded\",()=>{gform.scriptsLoaded=!0,gform.callIfLoaded(o)}),document.addEventListener(\"gform\/theme\/scripts_loaded\",()=>{gform.themeScriptsLoaded=!0,gform.callIfLoaded(o)}),window.addEventListener(\"DOMContentLoaded\",()=>{gform.domLoaded=!0,gform.callIfLoaded(o)}))},hooks:{action:{},filter:{}},addAction:function(o,r,e,t){gform.addHook(\"action\",o,r,e,t)},addFilter:function(o,r,e,t){gform.addHook(\"filter\",o,r,e,t)},doAction:function(o){gform.doHook(\"action\",o,arguments)},applyFilters:function(o){return gform.doHook(\"filter\",o,arguments)},removeAction:function(o,r){gform.removeHook(\"action\",o,r)},removeFilter:function(o,r,e){gform.removeHook(\"filter\",o,r,e)},addHook:function(o,r,e,t,n){null==gform.hooks[o][r]&&(gform.hooks[o][r]=[]);var d=gform.hooks[o][r];null==n&&(n=r+\"_\"+d.length),gform.hooks[o][r].push({tag:n,callable:e,priority:t=null==t?10:t})},doHook:function(r,o,e){var t;if(e=Array.prototype.slice.call(e,1),null!=gform.hooks[r][o]&&((o=gform.hooks[r][o]).sort(function(o,r){return o.priority-r.priority}),o.forEach(function(o){\"function\"!=typeof(t=o.callable)&&(t=window[t]),\"action\"==r?t.apply(null,e):e[0]=t.apply(null,e)})),\"filter\"==r)return e[0]},removeHook:function(o,r,t,n){var e;null!=gform.hooks[o][r]&&(e=(e=gform.hooks[o][r]).filter(function(o,r,e){return!!(null!=n&&n!=o.tag||null!=t&&t!=o.priority)}),gform.hooks[o][r]=e)}});\n\/* ]]> *\/\n<\/script>\n\n                <div class='gf_browser_gecko gform_wrapper gform-theme gform-theme--foundation gform-theme--framework gform-theme--orbital' data-form-theme='orbital' data-form-index='0' id='gform_wrapper_16' ><style>#gform_wrapper_16[data-form-index=\"0\"].gform-theme,[data-parent-form=\"16_0\"]{--gf-color-primary: #204ce5;--gf-color-primary-rgb: 32, 76, 229;--gf-color-primary-contrast: #fff;--gf-color-primary-contrast-rgb: 255, 255, 255;--gf-color-primary-darker: #001AB3;--gf-color-primary-lighter: #527EFF;--gf-color-secondary: #fff;--gf-color-secondary-rgb: 255, 255, 255;--gf-color-secondary-contrast: #112337;--gf-color-secondary-contrast-rgb: 17, 35, 55;--gf-color-secondary-darker: #F5F5F5;--gf-color-secondary-lighter: #FFFFFF;--gf-color-out-ctrl-light: rgba(17, 35, 55, 0.1);--gf-color-out-ctrl-light-rgb: 17, 35, 55;--gf-color-out-ctrl-light-darker: rgba(104, 110, 119, 0.35);--gf-color-out-ctrl-light-lighter: #F5F5F5;--gf-color-out-ctrl-dark: #585e6a;--gf-color-out-ctrl-dark-rgb: 88, 94, 106;--gf-color-out-ctrl-dark-darker: #112337;--gf-color-out-ctrl-dark-lighter: rgba(17, 35, 55, 0.65);--gf-color-in-ctrl: #fff;--gf-color-in-ctrl-rgb: 255, 255, 255;--gf-color-in-ctrl-contrast: #112337;--gf-color-in-ctrl-contrast-rgb: 17, 35, 55;--gf-color-in-ctrl-darker: #F5F5F5;--gf-color-in-ctrl-lighter: #FFFFFF;--gf-color-in-ctrl-primary: #204ce5;--gf-color-in-ctrl-primary-rgb: 32, 76, 229;--gf-color-in-ctrl-primary-contrast: #fff;--gf-color-in-ctrl-primary-contrast-rgb: 255, 255, 255;--gf-color-in-ctrl-primary-darker: #001AB3;--gf-color-in-ctrl-primary-lighter: #527EFF;--gf-color-in-ctrl-light: rgba(17, 35, 55, 0.1);--gf-color-in-ctrl-light-rgb: 17, 35, 55;--gf-color-in-ctrl-light-darker: rgba(104, 110, 119, 0.35);--gf-color-in-ctrl-light-lighter: #F5F5F5;--gf-color-in-ctrl-dark: #585e6a;--gf-color-in-ctrl-dark-rgb: 88, 94, 106;--gf-color-in-ctrl-dark-darker: #112337;--gf-color-in-ctrl-dark-lighter: rgba(17, 35, 55, 0.65);--gf-radius: 3px;--gf-font-size-secondary: 14px;--gf-font-size-tertiary: 13px;--gf-icon-ctrl-number: url(\"data:image\/svg+xml,%3Csvg width='8' height='14' viewBox='0 0 8 14' fill='none' xmlns='http:\/\/www.w3.org\/2000\/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M4 0C4.26522 5.96046e-08 4.51957 0.105357 4.70711 0.292893L7.70711 3.29289C8.09763 3.68342 8.09763 4.31658 7.70711 4.70711C7.31658 5.09763 6.68342 5.09763 6.29289 4.70711L4 2.41421L1.70711 4.70711C1.31658 5.09763 0.683417 5.09763 0.292893 4.70711C-0.0976311 4.31658 -0.097631 3.68342 0.292893 3.29289L3.29289 0.292893C3.48043 0.105357 3.73478 0 4 0ZM0.292893 9.29289C0.683417 8.90237 1.31658 8.90237 1.70711 9.29289L4 11.5858L6.29289 9.29289C6.68342 8.90237 7.31658 8.90237 7.70711 9.29289C8.09763 9.68342 8.09763 10.3166 7.70711 10.7071L4.70711 13.7071C4.31658 14.0976 3.68342 14.0976 3.29289 13.7071L0.292893 10.7071C-0.0976311 10.3166 -0.0976311 9.68342 0.292893 9.29289Z' fill='rgba(17, 35, 55, 0.65)'\/%3E%3C\/svg%3E\");--gf-icon-ctrl-select: url(\"data:image\/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http:\/\/www.w3.org\/2000\/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0.292893 0.292893C0.683417 -0.097631 1.31658 -0.097631 1.70711 0.292893L5 3.58579L8.29289 0.292893C8.68342 -0.0976311 9.31658 -0.0976311 9.70711 0.292893C10.0976 0.683417 10.0976 1.31658 9.70711 1.70711L5.70711 5.70711C5.31658 6.09763 4.68342 6.09763 4.29289 5.70711L0.292893 1.70711C-0.0976311 1.31658 -0.0976311 0.683418 0.292893 0.292893Z' fill='rgba(17, 35, 55, 0.65)'\/%3E%3C\/svg%3E\");--gf-icon-ctrl-search: url(\"data:image\/svg+xml,%3Csvg width='640' height='640' xmlns='http:\/\/www.w3.org\/2000\/svg'%3E%3Cpath d='M256 128c-70.692 0-128 57.308-128 128 0 70.691 57.308 128 128 128 70.691 0 128-57.309 128-128 0-70.692-57.309-128-128-128zM64 256c0-106.039 85.961-192 192-192s192 85.961 192 192c0 41.466-13.146 79.863-35.498 111.248l154.125 154.125c12.496 12.496 12.496 32.758 0 45.254s-32.758 12.496-45.254 0L367.248 412.502C335.862 434.854 297.467 448 256 448c-106.039 0-192-85.962-192-192z' fill='rgba(17, 35, 55, 0.65)'\/%3E%3C\/svg%3E\");--gf-label-space-y-secondary: var(--gf-label-space-y-md-secondary);--gf-ctrl-border-color: #686e77;--gf-ctrl-size: var(--gf-ctrl-size-md);--gf-ctrl-label-color-primary: #112337;--gf-ctrl-label-color-secondary: #112337;--gf-ctrl-choice-size: var(--gf-ctrl-choice-size-md);--gf-ctrl-checkbox-check-size: var(--gf-ctrl-checkbox-check-size-md);--gf-ctrl-radio-check-size: var(--gf-ctrl-radio-check-size-md);--gf-ctrl-btn-font-size: var(--gf-ctrl-btn-font-size-md);--gf-ctrl-btn-padding-x: var(--gf-ctrl-btn-padding-x-md);--gf-ctrl-btn-size: var(--gf-ctrl-btn-size-md);--gf-ctrl-btn-border-color-secondary: #686e77;--gf-ctrl-file-btn-bg-color-hover: #EBEBEB;--gf-field-img-choice-size: var(--gf-field-img-choice-size-md);--gf-field-img-choice-card-space: var(--gf-field-img-choice-card-space-md);--gf-field-img-choice-check-ind-size: var(--gf-field-img-choice-check-ind-size-md);--gf-field-img-choice-check-ind-icon-size: var(--gf-field-img-choice-check-ind-icon-size-md);--gf-field-pg-steps-number-color: rgba(17, 35, 55, 0.8);}<\/style><div id='gf_16' class='gform_anchor' tabindex='-1'><\/div>\n                        <div class='gform_heading'>\n\t\t\t\t\t\t\t<p class='gform_required_legend'>&quot;<span class=\"gfield_required gfield_required_asterisk\">*<\/span>&quot; indicates required fields<\/p>\n                        <\/div><form method='post' enctype='multipart\/form-data' target='gform_ajax_frame_16' id='gform_16'  action='\/ko\/wp-json\/wp\/v2\/pages\/719#gf_16' data-formid='16' novalidate>\n                        <div class='gform-body gform_body'><div id='gform_fields_16' class='gform_fields top_label form_sublabel_below description_below validation_below'><div id=\"field_16_12\" class=\"gfield gfield--type-honeypot gform_validation_container field_sublabel_below gfield--has-description field_description_below field_validation_below gfield_visibility_visible\"  ><label class='gfield_label gform-field-label' for='input_16_12'>Company<\/label><div class='ginput_container'><input name='input_12' id='input_16_12' type='text' value='' autocomplete='new-password'\/><\/div><div class='gfield_description' id='gfield_description_16_12'>This field is for validation purposes and should be left unchanged.<\/div><\/div><div id=\"field_16_1\" class=\"gfield gfield--type-text gfield--input-type-text gfield--width-full gfield_contains_required field_sublabel_below gfield--no-description field_description_below field_validation_below gfield_visibility_visible\"  ><label class='gfield_label gform-field-label' for='input_16_1'>Customer Name<span class=\"gfield_required\"><span class=\"gfield_required gfield_required_asterisk\">*<\/span><\/span><\/label><div class='ginput_container ginput_container_text'><input name='input_1' id='input_16_1' type='text' value='' class='large'    placeholder='Your Name' aria-required=\"true\" aria-invalid=\"false\"   \/><\/div><\/div><div id=\"field_16_2\" class=\"gfield gfield--type-phone gfield--input-type-phone gfield--width-full gfield_contains_required field_sublabel_below gfield--no-description field_description_below field_validation_below gfield_visibility_visible\"  ><label class='gfield_label gform-field-label' for='input_16_2'>Customer Phone Number<span class=\"gfield_required\"><span class=\"gfield_required gfield_required_asterisk\">*<\/span><\/span><\/label><div class='ginput_container ginput_container_phone'><input name='input_2' id='input_16_2' type='tel' value='' class='large'  placeholder='(555) 555-5555' aria-required=\"true\" aria-invalid=\"false\"   \/><\/div><\/div><div id=\"field_16_3\" class=\"gfield gfield--type-email gfield--input-type-email gfield--width-full gfield_contains_required field_sublabel_below gfield--no-description field_description_below field_validation_below gfield_visibility_visible\"  ><label class='gfield_label gform-field-label' for='input_16_3'>Customer Email<span class=\"gfield_required\"><span class=\"gfield_required gfield_required_asterisk\">*<\/span><\/span><\/label><div class='ginput_container ginput_container_email'>\n                            <input name='input_3' id='input_16_3' type='email' value='' class='large'   placeholder='you@mail.com' aria-required=\"true\" aria-invalid=\"false\"  \/>\n                        <\/div><\/div><div id=\"field_16_4\" class=\"gfield gfield--type-date gfield--input-type-date gfield--input-type-datepicker gfield--datepicker-default-icon gfield--width-full gfield_contains_required field_sublabel_below gfield--no-description field_description_below field_validation_below gfield_visibility_visible\"  ><label class='gfield_label gform-field-label' for='input_16_4'>Exit Date<span class=\"gfield_required\"><span class=\"gfield_required gfield_required_asterisk\">*<\/span><\/span><\/label><div class='ginput_container ginput_container_date'>\n                            <input name='input_4' id='input_16_4' type='text' value='' class='datepicker gform-datepicker mdy datepicker_with_icon gdatepicker_with_icon'   placeholder='Month\/Day\/Year' aria-describedby=\"input_16_4_date_format\" aria-invalid=\"false\" aria-required=\"true\"\/>\n                            <span id='input_16_4_date_format' class='screen-reader-text'>MM slash DD slash YYYY<\/span>\n                        <\/div>\n                        <input type='hidden' id='gforms_calendar_icon_input_16_4' class='gform_hidden' value='https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/plugins\/gravityforms\/images\/datepicker\/datepicker.svg'\/><\/div><fieldset id=\"field_16_5\" class=\"gfield gfield--type-radio gfield--type-choice gfield--input-type-radio gfield--width-full gfield_contains_required field_sublabel_below gfield--no-description field_description_below field_validation_below gfield_visibility_visible\"  ><legend class='gfield_label gform-field-label' >Lot Parked In<span class=\"gfield_required\"><span class=\"gfield_required gfield_required_asterisk\">*<\/span><\/span><\/legend><div class='ginput_container ginput_container_radio'><div class='gfield_radio' id='input_16_5'>\n\t\t\t<div class='gchoice gchoice_16_5_0'>\n\t\t\t\t\t<input class='gfield-choice-input' name='input_5' type='radio' value='Green Lot'  id='choice_16_5_0' onchange='gformToggleRadioOther( this )'    \/>\n\t\t\t\t\t<label for='choice_16_5_0' id='label_16_5_0' class='gform-field-label gform-field-label--type-inline'>Green Lot<\/label>\n\t\t\t<\/div>\n\t\t\t<div class='gchoice gchoice_16_5_1'>\n\t\t\t\t\t<input class='gfield-choice-input' name='input_5' type='radio' value='Blue Deck'  id='choice_16_5_1' onchange='gformToggleRadioOther( this )'    \/>\n\t\t\t\t\t<label for='choice_16_5_1' id='label_16_5_1' class='gform-field-label gform-field-label--type-inline'>Blue Deck<\/label>\n\t\t\t<\/div>\n\t\t\t<div class='gchoice gchoice_16_5_2'>\n\t\t\t\t\t<input class='gfield-choice-input' name='input_5' type='radio' value='McNamara Garage'  id='choice_16_5_2' onchange='gformToggleRadioOther( this )'    \/>\n\t\t\t\t\t<label for='choice_16_5_2' id='label_16_5_2' class='gform-field-label gform-field-label--type-inline'>McNamara Garage<\/label>\n\t\t\t<\/div>\n\t\t\t<div class='gchoice gchoice_16_5_3'>\n\t\t\t\t\t<input class='gfield-choice-input' name='input_5' type='radio' value='Off-Site Parking'  id='choice_16_5_3' onchange='gformToggleRadioOther( this )'    \/>\n\t\t\t\t\t<label for='choice_16_5_3' id='label_16_5_3' class='gform-field-label gform-field-label--type-inline'>Off-Site Parking<\/label>\n\t\t\t<\/div><\/div><\/div><\/fieldset><div id=\"field_16_10\" class=\"gfield gfield--type-text gfield--input-type-text gfield--width-full gfield_contains_required field_sublabel_below gfield--no-description field_description_below field_validation_below gfield_visibility_visible\"  ><label class='gfield_label gform-field-label' for='input_16_10'>Vehicle License Plate Number<span class=\"gfield_required\"><span class=\"gfield_required gfield_required_asterisk\">*<\/span><\/span><\/label><div class='ginput_container ginput_container_text'><input name='input_10' id='input_16_10' type='text' value='' class='large'     aria-required=\"true\" aria-invalid=\"false\"   \/><\/div><\/div><div id=\"field_16_7\" class=\"gfield gfield--type-text gfield--input-type-text gfield--width-full gfield_contains_required field_sublabel_below gfield--no-description field_description_below field_validation_below gfield_visibility_visible\"  ><label class='gfield_label gform-field-label' for='input_16_7'>Amount of Receipt<span class=\"gfield_required\"><span class=\"gfield_required gfield_required_asterisk\">*<\/span><\/span><\/label><div class='ginput_container ginput_container_text'><input name='input_7' id='input_16_7' type='text' value='' class='large'    placeholder='$0.00' aria-required=\"true\" aria-invalid=\"false\"   \/><\/div><\/div><fieldset id=\"field_16_11\" class=\"gfield gfield--type-radio gfield--type-choice gfield--input-type-radio gfield--width-full field_sublabel_below gfield--no-description field_description_below field_validation_below gfield_visibility_visible\"  ><legend class='gfield_label gform-field-label' >Payment Type<\/legend><div class='ginput_container ginput_container_radio'><div class='gfield_radio' id='input_16_11'>\n\t\t\t<div class='gchoice gchoice_16_11_0'>\n\t\t\t\t\t<input class='gfield-choice-input' name='input_11' type='radio' value='Cash'  id='choice_16_11_0' onchange='gformToggleRadioOther( this )'    \/>\n\t\t\t\t\t<label for='choice_16_11_0' id='label_16_11_0' class='gform-field-label gform-field-label--type-inline'>Cash<\/label>\n\t\t\t<\/div>\n\t\t\t<div class='gchoice gchoice_16_11_1'>\n\t\t\t\t\t<input class='gfield-choice-input' name='input_11' type='radio' value='Credit Card'  id='choice_16_11_1' onchange='gformToggleRadioOther( this )'    \/>\n\t\t\t\t\t<label for='choice_16_11_1' id='label_16_11_1' class='gform-field-label gform-field-label--type-inline'>Credit Card<\/label>\n\t\t\t<\/div><\/div><\/div><\/fieldset><div id=\"field_16_8\" class=\"gfield gfield--type-captcha gfield--input-type-captcha gfield--width-full field_sublabel_below gfield--no-description field_description_below hidden_label field_validation_below gfield_visibility_visible\"  ><label class='gfield_label gform-field-label' for='input_16_8'>CAPTCHA<\/label><div id='input_16_8' class='ginput_container ginput_recaptcha' data-sitekey='6Lexu1ssAAAAABCwhXQvA6Dg6yPMZyPLUnMy-zyl'  data-theme='light' data-tabindex='-1' data-size='invisible' data-badge='bottomright'><\/div><\/div><\/div><\/div>\n        <div class='gform-footer gform_footer top_label'> <input type='submit' id='gform_submit_button_16' class='gform_button button' onclick='gform.submission.handleButtonClick(this);' data-submission-type='submit' value='Submit'  \/> <input type='hidden' name='gform_ajax' value='form_id=16&amp;title=&amp;description=&amp;tabindex=0&amp;theme=orbital&amp;styles=[]&amp;hash=e1dc9b2ce9b809611acabdb28e4b30e0' \/>\n            <input type='hidden' class='gform_hidden' name='gform_submission_method' data-js='gform_submission_method_16' value='iframe' \/>\n            <input type='hidden' class='gform_hidden' name='gform_theme' data-js='gform_theme_16' id='gform_theme_16' value='orbital' \/>\n            <input type='hidden' class='gform_hidden' name='gform_style_settings' data-js='gform_style_settings_16' id='gform_style_settings_16' value='[]' \/>\n            <input type='hidden' class='gform_hidden' name='is_submit_16' value='1' \/>\n            <input type='hidden' class='gform_hidden' name='gform_submit' value='16' \/>\n            \n            <input type='hidden' class='gform_hidden' name='gform_unique_id' value='' \/>\n            <input type='hidden' class='gform_hidden' name='state_16' value='WyJ7XCI1XCI6W1wiZWE2ZWYxYzRlMThjMzVkYTNmMTRlMTU1NzczNjAyMmZcIixcImJmOWVmNTdlYzA2NjdhN2Q5OWZlYWMwYTk5NWZiMDgzXCIsXCJiNWEwMzJmOWE3NWYyZjQxNGI5ZmQxMTYyZDBmOWIxMlwiLFwiMTEwNmRjY2M5ZmY4ODVhZmYzZjU4N2JkZjgwODcwZjdcIl0sXCIxMVwiOltcImNjY2RkNmJhMmViMWUxYWI4NWEwNDRmNDc4YTMxMTM3XCIsXCIzYTUyMDgwNDM2ZWYxMGFkMDVmNzg0NDcyODcwMjg1ZVwiXX0iLCJkZTEzYzI5YjFhOGU0YzA0MTIxMjQxZTU5NDI3NzdlNCJd' \/>\n            <input type='hidden' autocomplete='off' class='gform_hidden' name='gform_target_page_number_16' id='gform_target_page_number_16' value='0' \/>\n            <input type='hidden' autocomplete='off' class='gform_hidden' name='gform_source_page_number_16' id='gform_source_page_number_16' value='1' \/>\n            <input type='hidden' name='gform_field_values' value='' \/>\n            \n        <\/div>\n                        <\/form>\n                        <\/div>\n\t\t                <iframe style='display:none;width:0px;height:0px;' src='about:blank' name='gform_ajax_frame_16' id='gform_ajax_frame_16' title='This iframe contains the logic required to handle Ajax powered Gravity Forms.'><\/iframe>\n\t\t                <script type=\"text\/javascript\">\n\/* <![CDATA[ *\/\n gform.initializeOnLoaded( function() {gformInitSpinner( 16, 'https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/plugins\/gravityforms\/images\/spinner.svg', false );jQuery('#gform_ajax_frame_16').on('load',function(){var contents = jQuery(this).contents().find('*').html();var is_postback = contents.indexOf('GF_AJAX_POSTBACK') >= 0;if(!is_postback){return;}var form_content = jQuery(this).contents().find('#gform_wrapper_16');var is_confirmation = jQuery(this).contents().find('#gform_confirmation_wrapper_16').length > 0;var is_redirect = contents.indexOf('gformRedirect(){') >= 0;var is_form = form_content.length > 0 && ! is_redirect && ! is_confirmation;var mt = parseInt(jQuery('html').css('margin-top'), 10) + parseInt(jQuery('body').css('margin-top'), 10) + 100;if(is_form){jQuery('#gform_wrapper_16').html(form_content.html());if(form_content.hasClass('gform_validation_error')){jQuery('#gform_wrapper_16').addClass('gform_validation_error');} else {jQuery('#gform_wrapper_16').removeClass('gform_validation_error');}setTimeout( function() { \/* delay the scroll by 50 milliseconds to fix a bug in chrome *\/ jQuery(document).scrollTop(jQuery('#gform_wrapper_16').offset().top - mt); }, 50 );if(window['gformInitDatepicker']) {gformInitDatepicker();}if(window['gformInitPriceFields']) {gformInitPriceFields();}var current_page = jQuery('#gform_source_page_number_16').val();gformInitSpinner( 16, 'https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/plugins\/gravityforms\/images\/spinner.svg', false );jQuery(document).trigger('gform_page_loaded', [16, current_page]);window['gf_submitting_16'] = false;}else if(!is_redirect){var confirmation_content = jQuery(this).contents().find('.GF_AJAX_POSTBACK').html();if(!confirmation_content){confirmation_content = contents;}jQuery('#gform_wrapper_16').replaceWith(confirmation_content);jQuery(document).scrollTop(jQuery('#gf_16').offset().top - mt);jQuery(document).trigger('gform_confirmation_loaded', [16]);window['gf_submitting_16'] = false;wp.a11y.speak(jQuery('#gform_confirmation_message_16').text());}else{jQuery('#gform_16').append(contents);if(window['gformRedirect']) {gformRedirect();}}jQuery(document).trigger(\"gform_pre_post_render\", [{ formId: \"16\", currentPage: \"current_page\", abort: function() { this.preventDefault(); } }]);        if (event && event.defaultPrevented) {                return;        }        const gformWrapperDiv = document.getElementById( \"gform_wrapper_16\" );        if ( gformWrapperDiv ) {            const visibilitySpan = document.createElement( \"span\" );            visibilitySpan.id = \"gform_visibility_test_16\";            gformWrapperDiv.insertAdjacentElement( \"afterend\", visibilitySpan );        }        const visibilityTestDiv = document.getElementById( \"gform_visibility_test_16\" );        let postRenderFired = false;        function triggerPostRender() {            if ( postRenderFired ) {                return;            }            postRenderFired = true;            gform.core.triggerPostRenderEvents( 16, current_page );            if ( visibilityTestDiv ) {                visibilityTestDiv.parentNode.removeChild( visibilityTestDiv );            }        }        function debounce( func, wait, immediate ) {            var timeout;            return function() {                var context = this, args = arguments;                var later = function() {                    timeout = null;                    if ( !immediate ) func.apply( context, args );                };                var callNow = immediate && !timeout;                clearTimeout( timeout );                timeout = setTimeout( later, wait );                if ( callNow ) func.apply( context, args );            };        }        const debouncedTriggerPostRender = debounce( function() {            triggerPostRender();        }, 200 );        if ( visibilityTestDiv && visibilityTestDiv.offsetParent === null ) {            const observer = new MutationObserver( ( mutations ) => {                mutations.forEach( ( mutation ) => {                    if ( mutation.type === 'attributes' && visibilityTestDiv.offsetParent !== null ) {                        debouncedTriggerPostRender();                        observer.disconnect();                    }                });            });            observer.observe( document.body, {                attributes: true,                childList: false,                subtree: true,                attributeFilter: [ 'style', 'class' ],            });        } else {            triggerPostRender();        }    } );} ); \n\/* ]]> *\/\n<\/script>\n<\/div>\n\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t<\/div>\n<\/div>\n\n\n\n<div id=\"accessibilityinformation\" class=\"blockGlobal paddingTopLG paddingBottomLG cardIconVerticalFluid noRadius\" style=\"background-color:#F3F7FC;\">\n\t\t<div class=\"container\">\n      <div class=\"cardIconVerticalWrap\">\n\n\t\t\t\t<!-- Single column layout (default) -->\n\t\t\t\t\n\n<!--Option 1-->\n\n\n<!--Option 2-->\n<div class=\"grid align-end wysiwyg wysiwygOption2 intro\">\n\t<div class=\"col-md-7 mediaLeft\">\n\t\t<div class=\"maincopy light\">\n\t\t\t\t\t\t\t<h2 class=\"h2 \">Accessibility Information<\/h2>\n\t\t\t\t\t\t\t\t\t\t<p>Explore our accessible transportation options below! Shuttle service runs free and 24\/7 from all surface lots &#8211; including Green Lots &#8211; to each terminal\u2019s Ground Transportation Center.<\/p>\n\t\t\t\t\t<\/div>\n\t<\/div>\n\t<div class=\"col-md-5 mediaRight\">\n\t\t\t\t\t\t\t<a\nhref=\"https:\/\/wcaa.preprod.fruitionqa.com\/services\/wheelchairs\/\"\nclass=\"primaryButton light\"\ntitle=\"Accessibility Services\">\nAccessibility Services<\/a>\n\t\t\t\n\t\t\t\n\t\t\t\t<\/div>\n<\/div>\n\n\n<!--Option 3-->\n\n\t\t\t\t<div class=\"grid\">\t\t\t\t\t\t\t<div class=\"col-sm-4 col-grid\">\n\t\t\t\t\t\t\t\t\n<div class=\"cardIcon boxed\">\n\t\n<div class=\"iconWrapper iconGhost large\">\n\t<span class=\"fa-kit fa-custom fa-parking\"><\/span>\n<\/div>\n\t<h2 class=\"h5\">Parking Spaces<\/h2>\n\t\t<div class=\"cardIconCopy\">\n\t\t<p>Accessible spaces are available in all parking structures and surface lots for vehicles displaying a valid Disability Parking Placard or Disability License Plate.<\/p>\n<p>Unauthorized vehicles may face fines or towing.<\/p>\n\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"col-sm-4 col-grid\">\n\t\t\t\t\t\t\t\t\n<div class=\"cardIcon boxed\">\n\t\n<div class=\"iconWrapper iconGhost large\">\n\t<span class=\"fa-kit fa-custom fa-bus\"><\/span>\n<\/div>\n\t<h2 class=\"h5\">Vehicle height limits<\/h2>\n\t\t<div class=\"cardIconCopy\">\n\t\t<p>Most covered garages accommodate vehicles up to 7 feet (7\u2032-0\u2033) tall.<\/p>\n<p>Van-accessible spaces (up to 8 feet 2 inches) are located in:<\/p>\n<ul>\n<li>McNamara Garage (Level 2 near the B-elevators).<\/li>\n<li>Big Blue Deck (Zones A and B on Level 1).<\/li>\n<\/ul>\n<p>Vehicles taller than 8\u2032 2\u2033 should use the Green Lots, where no height restrictions apply, and take the free shuttle to the terminal.<\/p>\n\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"col-sm-4 col-grid\">\n\t\t\t\t\t\t\t\t\n<div class=\"cardIcon boxed\">\n\t\n<div class=\"iconWrapper iconGhost large\">\n\t<span class=\"fa-kit fa-custom fa-location\"><\/span>\n<\/div>\n\t<h2 class=\"h5\">Michigan Residents<\/h2>\n\t\t<div class=\"cardIconCopy\">\n\t\t<p>Michigan residents with both a handicap placard and a free-parking sticker may qualify for free parking. This benefit is specific to State of Michigan residents and requires the additional sticker.<\/p>\n\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<div id=\"frequentlyaskedquestions\" class=\"blockGlobal paddingTopLG paddingBottomLG accordionFluid noRadius\" style=\"background-color:#fff;\">\n\t\t<div class=\"container\">\n\t\t\t\t\t<div class=\"grid\">\n\t\t\t\t<div class=\"col-md-6 mediaLeft\">\n\t\t\t\t\t\n\n<div class=\"wysiwyg\">\n\t<div class=\"maincopy light\">\n\n\t\t\n\t\t\t\t\t\t\t\t\t<h2 class=\"h2 \">Frequently Asked Questions<\/h2>\n\t\t\t\t\t\t\t\t\t\t<p>Find quick answers to common questions about parking, shuttles, and getting around DTW.<\/p>\n\t\t\t<\/div>\n\n\t\n\t\n\t\n\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"col-md-6 mediaRight\">\n\t\t\t\t\t\n<div class=\"beefup openAccordion accordionStyle boxed\">\n\t<button title=\"Show How do I know which terminal to park near?\" class=\"openTitle beefup__head h6 \"><span class=\"beefup__head--title\">How do I know which terminal to park near?<\/span><\/button>\n\t<div class=\"beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n\t\t<p>Check your airline and terminal before arriving.<\/p>\n<p>Learn more about <a href=\"https:\/\/wcaa.preprod.fruitionqa.com\/gates-and-terminals\/\">Gates and Terminals.<\/a><\/p>\n\t\t\t<\/div>\n<\/div>\n\n<div class=\"beefup openAccordion accordionStyle boxed\">\n\t<button title=\"Show Can I reserve parking in advance?\" class=\"openTitle beefup__head h6 \"><span class=\"beefup__head--title\">Can I reserve parking in advance?<\/span><\/button>\n\t<div class=\"beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n\t\t<p>Not at this time. All parking is first come, first served.<\/p>\n\t\t\t<\/div>\n<\/div>\n\n<div class=\"beefup openAccordion accordionStyle boxed\">\n\t<button title=\"Show Is there EV charging at every lot?\" class=\"openTitle beefup__head h6 \"><span class=\"beefup__head--title\">Is there EV charging at every lot?<\/span><\/button>\n\t<div class=\"beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n\t\t<p>EV charging is only available in the Big Blue Deck and McNamara Deck.<\/p>\n\t\t\t<\/div>\n<\/div>\n\n<div class=\"beefup openAccordion accordionStyle boxed\">\n\t<button title=\"Show What if I lose my parking ticket?\" class=\"openTitle beefup__head h6 \"><span class=\"beefup__head--title\">What if I lose my parking ticket?<\/span><\/button>\n\t<div class=\"beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n\t\t<p>You\u2019ll be charged the full daily rate for each day parked.<\/p>\n\t\t\t<\/div>\n<\/div>\n\n<div class=\"beefup openAccordion accordionStyle boxed\">\n\t<button title=\"Show Are the shuttles accessible?\" class=\"openTitle beefup__head h6 \"><span class=\"beefup__head--title\">Are the shuttles accessible?<\/span><\/button>\n\t<div class=\"beefup__body maincopy\" role=\"region\" hidden=\"hidden\" style=\"display: none;\">\n\t\t<p>Yes, all shuttles are ADA-compliant.<\/p>\n\t\t\t<\/div>\n<\/div>\n\t\t\t\t<\/div>      \n\t\t\t<\/div>\n\t\t\t<\/div>\n<\/div>\n\n\n\n\n\t<div id=\"contactus\" class=\"blockGlobal paddingTopMD paddingBottomMD container calloutBannerMiniContainer \" style=\"background-color:#fff;\">\n\t\t\n\t\t<div class=\"innerContent\"  style=\"background-color: #004A78;\" >\n\t\t\t<div class=\"maincopy dark\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"h4\">Need support? Contact us<\/h2>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t<div class=\"buttonGroup\">\t\t\t\n\t\t\t\t\t\t\t\t\t<a\nhref=\"https:\/\/wcaa.preprod.fruitionqa.com\/airlines\/\"\nclass=\"primaryButton dark\"\ntitle=\"Contact Your Airline\">\nContact Your Airline<\/a>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<a\nhref=\"https:\/\/wcaa.preprod.fruitionqa.com\/about-dtw\/contact\/\"\nclass=\"primaryButton outlined dark\"\ntitle=\"Contact Us\">\nContact Us<\/a>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/div>\n\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Find DTW parking rates, real-time availability, and options for short-term, long-term, and valet. Includes EV charging, accessibility info, and cashless payment.<\/p>","protected":false},"author":7,"featured_media":3044,"parent":717,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-719","page","type-page","status-publish","has-post-thumbnail","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Parking - WCAA<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/parking-and-transportation\/parking\/\" \/>\n<meta property=\"og:locale\" content=\"ko_KR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Parking - WCAA\" \/>\n<meta property=\"og:description\" content=\"Find DTW parking rates, real-time availability, and options for short-term, long-term, and valet. Includes EV charging, accessibility info, and cashless payment.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/parking-and-transportation\/parking\/\" \/>\n<meta property=\"og:site_name\" content=\"WCAA\" \/>\n<meta property=\"article:modified_time\" content=\"2026-03-31T20:51:20+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/Parking-at-DTW.jpeg\" \/>\n\t<meta property=\"og:image:width\" content=\"2048\" \/>\n\t<meta property=\"og:image:height\" content=\"1145\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/\",\"url\":\"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/\",\"name\":\"Parking - WCAA\",\"isPartOf\":{\"@id\":\"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/Parking-at-DTW.jpeg\",\"datePublished\":\"2025-11-24T20:51:44+00:00\",\"dateModified\":\"2026-03-31T20:51:20+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#breadcrumb\"},\"inLanguage\":\"ko-KR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#primaryimage\",\"url\":\"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/Parking-at-DTW.jpeg\",\"contentUrl\":\"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/Parking-at-DTW.jpeg\",\"width\":2048,\"height\":1145},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/wcaa.preprod.fruitionqa.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Transportation\",\"item\":\"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Parking\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/#website\",\"url\":\"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/\",\"name\":\"WCAA\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ko-KR\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Parking - WCAA","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/parking-and-transportation\/parking\/","og_locale":"ko_KR","og_type":"article","og_title":"Parking - WCAA","og_description":"Find DTW parking rates, real-time availability, and options for short-term, long-term, and valet. Includes EV charging, accessibility info, and cashless payment.","og_url":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/parking-and-transportation\/parking\/","og_site_name":"WCAA","article_modified_time":"2026-03-31T20:51:20+00:00","og_image":[{"width":2048,"height":1145,"url":"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/Parking-at-DTW.jpeg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/","url":"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/","name":"Parking - WCAA","isPartOf":{"@id":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/#website"},"primaryImageOfPage":{"@id":"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#primaryimage"},"image":{"@id":"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#primaryimage"},"thumbnailUrl":"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/Parking-at-DTW.jpeg","datePublished":"2025-11-24T20:51:44+00:00","dateModified":"2026-03-31T20:51:20+00:00","breadcrumb":{"@id":"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#breadcrumb"},"inLanguage":"ko-KR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/"]}]},{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#primaryimage","url":"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/Parking-at-DTW.jpeg","contentUrl":"https:\/\/wcaa.preprod.fruitionqa.com\/wp-content\/uploads\/2025\/12\/Parking-at-DTW.jpeg","width":2048,"height":1145},{"@type":"BreadcrumbList","@id":"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/parking\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/wcaa.preprod.fruitionqa.com\/"},{"@type":"ListItem","position":2,"name":"Transportation","item":"https:\/\/wcaa.preprod.fruitionqa.com\/parking-and-transportation\/"},{"@type":"ListItem","position":3,"name":"Parking"}]},{"@type":"WebSite","@id":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/#website","url":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/","name":"WCAA","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ko-KR"}]}},"_links":{"self":[{"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/pages\/719","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/comments?post=719"}],"version-history":[{"count":72,"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/pages\/719\/revisions"}],"predecessor-version":[{"id":4864,"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/pages\/719\/revisions\/4864"}],"up":[{"embeddable":true,"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/pages\/717"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/media\/3044"}],"wp:attachment":[{"href":"https:\/\/wcaa.preprod.fruitionqa.com\/ko\/wp-json\/wp\/v2\/media?parent=719"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}