{"id":56,"date":"2024-03-11T11:14:26","date_gmt":"2024-03-11T11:14:26","guid":{"rendered":"https:\/\/hirtshals-kristiansand.com\/da\/?page_id=56"},"modified":"2026-05-19T12:51:17","modified_gmt":"2026-05-19T12:51:17","slug":"sejlplan","status":"publish","type":"page","link":"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/","title":{"rendered":"Hirtshals Kristiansand sejlplan"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Se den aktuelle k\u00f8replan for f\u00e6rgeruten Hirtshals Kristiansand (drevet af Fjord Line og Color Line).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Hirtshals Kristiansand afgangs- og ankomsttider<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Se afgangs- og ankomsttider fra <a href=\"https:\/\/hirtshals-kristiansand.com\/da\/hirtshals-havn\/\">Hirtshals havn<\/a>.<\/p>\n\n\n<div class=\"tp-wrap tp-theme-extended_cards tp-day-setup-modern\" data-config=\"{&quot;rest&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/wp-json\\\/timetables-pro\\\/v1\\\/timetables&quot;,&quot;routes&quot;:[171],&quot;days&quot;:6,&quot;autoload&quot;:true,&quot;theme&quot;:&quot;extended_cards&quot;,&quot;daySetup&quot;:&quot;modern&quot;,&quot;limit10&quot;:false,&quot;routeLabels&quot;:{&quot;171&quot;:&quot;Hirtshals til Kristiansand&quot;},&quot;opFilter&quot;:[&quot;8&quot;,&quot;27&quot;],&quot;labels&quot;:{&quot;ui_date&quot;:&quot;Dato&quot;,&quot;ui_days&quot;:&quot;Dage&quot;,&quot;ui_button&quot;:&quot;Vis afgange&quot;,&quot;status_idle&quot;:&quot;V\\u00e6lg en dato og klik p\\u00e5 Vis afgange&quot;,&quot;status_loading&quot;:&quot;Indl\\u00e6ser...&quot;,&quot;status_empty&quot;:&quot;Ingen afgange fundet&quot;,&quot;status_found&quot;:&quot;%d afgange fundet&quot;,&quot;th_date&quot;:&quot;Dato&quot;,&quot;th_dep&quot;:&quot;Afgang&quot;,&quot;th_arr&quot;:&quot;Ankomst&quot;,&quot;th_dur&quot;:&quot;Varighed&quot;,&quot;th_ship&quot;:&quot;Skib&quot;,&quot;th_op&quot;:&quot;Operat\\u00f8r&quot;,&quot;th_route&quot;:&quot;Rute&quot;,&quot;wd_sun&quot;:&quot;S\\u00d8N&quot;,&quot;wd_mon&quot;:&quot;MAN&quot;,&quot;wd_tue&quot;:&quot;TIR&quot;,&quot;wd_wed&quot;:&quot;ONS&quot;,&quot;wd_thu&quot;:&quot;TOR&quot;,&quot;wd_fri&quot;:&quot;FRE&quot;,&quot;wd_sat&quot;:&quot;L\\u00d8R&quot;,&quot;summary_footer_one&quot;:&quot;\\u00bb Se alle %s afgange i vores sejlplan&quot;,&quot;summary_footer_two&quot;:&quot;\\u00bb Se flere afgange for %1$s og %2$s i vores sejlplan&quot;,&quot;summary_footer_generic&quot;:&quot;Se vores sejlplan for flere afgange&quot;,&quot;summary_header&quot;:&quot;De n\\u00e6ste to afgange:&quot;,&quot;summary_no_upcoming&quot;:&quot;Ingen kommende afgange fundet&quot;,&quot;more_link&quot;:&quot;Flere afgange&quot;,&quot;local_time_pair&quot;:&quot;Lokal tid afgangshavn: %1$s \\\/ Lokal tid ankomsthavn: %2$s&quot;,&quot;local_time_combined&quot;:&quot;Lokal tid afgangs- og ankomsthavn: %s&quot;,&quot;tag_often_booked&quot;:&quot;\\u2b50 Ofte booket&quot;,&quot;tag_leaving_soon&quot;:&quot;\\ud83d\\udd34 Afg\\u00e5r snart&quot;,&quot;tag_currently_sailing&quot;:&quot;\\ud83d\\udfe2 Sejler nu&quot;,&quot;tag_arrived&quot;:&quot;\\u26ab Ankommet&quot;,&quot;tag_overnight&quot;:&quot;Over natten&quot;},&quot;showLogo&quot;:true,&quot;activityBoosters&quot;:true,&quot;title&quot;:&quot;Alle overfarter&quot;,&quot;shipMap&quot;:[],&quot;shipLinks&quot;:{&quot;bf&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/ms-bergensfjord\\\/&quot;,&quot;superspeed 1&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/color-line-superspeed-1\\\/&quot;},&quot;showShip&quot;:true,&quot;departurePort&quot;:&quot;Hirtshals&quot;,&quot;arrivalPort&quot;:&quot;Kristiansand&quot;,&quot;departureTimezone&quot;:&quot;Europe\\\/Copenhagen&quot;,&quot;arrivalTimezone&quot;:&quot;Europe\\\/Oslo&quot;}\">\n  <div class=\"tp-controls\" role=\"group\" aria-label=\"Timetable controls\">\n    <div>\n      <label for=\"tp-date-ops\">Dato<\/label><br>\n      <input id=\"tp-date-ops\" class=\"tp-date\" type=\"date\" aria-label=\"Dato\">\n    <\/div>\n    <div class=\"tp-day-field\">\n      <label for=\"tp-range-ops\">Dage<\/label><br>\n      <select id=\"tp-range-ops\" class=\"tp-range\" aria-label=\"Dage\">\n        <option value=\"1\">1<\/option><option value=\"3\">3<\/option><option value=\"7\">7<\/option><option value=\"14\">14<\/option>\n      <\/select>\n    <\/div>\n    <div><button id=\"tp-load-ops\" class=\"tp-btn\">Vis afgange<\/button><\/div>\n  <\/div>\n\n  <div class=\"tp-local-nav-row\">\n    <div class=\"tp-local-times\" id=\"tp-local-times-ops\" aria-live=\"polite\"><\/div>\n    <div class=\"tp-day-nav-host\"><\/div>\n  <\/div>\n\n      <h3 style=\"margin:8px 0 12px;color:#0f172a;\">Alle overfarter<\/h3>\n  \n  <div id=\"tp-status-ops\" class=\"tp-muted\" aria-live=\"polite\">V\u00e6lg en dato og klik p\u00e5 Vis afgange<\/div>\n\n  <div id=\"tp-ops-results\"><\/div>\n\n  <div class=\"tp-loader\" aria-hidden=\"true\">\n    <div class=\"tp-loader-card\">\n      <div class=\"tp-spinner\" aria-hidden=\"true\"><\/div>\n      <div class=\"tp-loader-text\">Indl\u00e6ser...<\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function(){\n  const wrap   = document.currentScript.previousElementSibling;\n  const cfg    = JSON.parse(wrap.getAttribute('data-config')||'{}');\n  const L      = cfg.labels||{};\n  const dateEl = wrap.querySelector('#tp-date-ops');\n  const daysEl = wrap.querySelector('#tp-range-ops');\n  const btn    = wrap.querySelector('#tp-load-ops');\n  const status = wrap.querySelector('#tp-status-ops');\n  const out    = wrap.querySelector('#tp-ops-results');\n  const loader = wrap.querySelector('.tp-loader');\n  const navHost = wrap.querySelector('.tp-day-nav-host');\n  const localTimesEl = wrap.querySelector('#tp-local-times-ops');\n\n  const showShip = (cfg.showShip !== false && cfg.showShip !== 0 && cfg.showShip !== '0');\n  const activityBoosters = !!cfg.activityBoosters;\n  const daySetup = (cfg.daySetup === 'modern') ? 'modern' : 'classic';\n  const limit10 = !!cfg.limit10;\n  let featuredKey = '';\n  let soonKey = '';\n  let revealAll = false;\n\n  if (daySetup === 'modern') {\n    const dayField = wrap.querySelector('.tp-day-field');\n    if (dayField) dayField.style.display = 'none';\n  }\n\n  dateEl.valueAsDate = new Date();\n  dateEl.min = new Date().toISOString().split('T')[0];\n  Array.from(daysEl.options).forEach(o=>{ if(parseInt(o.value,10)===parseInt(cfg.days||7,10)) o.selected=true; });\n\n  function pad(n){ return String(n).padStart(2,'0'); }\n  function iso(d){ return d.getFullYear()+'-'+pad(d.getMonth()+1)+'-'+pad(d.getDate()); }\n  function toLocal(s){ return new Date(s); }\n  function hhmm(d){ return d.toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'}); }\n  function dShort(d){ return d.toLocaleDateString([], {day:'numeric', month:'short'}); }\n  function dur(m){ const h=Math.floor(m\/60), r=m%60; return r? (h+'h '+r+'m') : (h+'h'); }\n\n  const WD = [\n    L.wd_sun || 'SUN',\n    L.wd_mon || 'MON',\n    L.wd_tue || 'TUE',\n    L.wd_wed || 'WED',\n    L.wd_thu || 'THU',\n    L.wd_fri || 'FRI',\n    L.wd_sat || 'SAT'\n  ];\n\n  function setLoader(v){ loader.classList.toggle('show', !!v); loader.setAttribute('aria-hidden', v?'false':'true'); }\n\n  function formatZoneTime(tz){\n    if (!tz) return '--:--';\n    try {\n      return new Intl.DateTimeFormat([], {hour:'2-digit', minute:'2-digit', hour12:false, timeZone: tz}).format(new Date());\n    } catch (e) {\n      return '--:--';\n    }\n  }\n\n  function renderLocalTimes(){\n    if (!localTimesEl) return;\n    if (!cfg.departureTimezone || !cfg.arrivalTimezone) {\n      localTimesEl.textContent = '';\n      return;\n    }\n    const depTime = formatZoneTime(cfg.departureTimezone);\n    const arrTime = formatZoneTime(cfg.arrivalTimezone);\n    const pairTpl = L.local_time_pair || 'Departure Port Local Time: %1$s \/ Arrival Port Local Time: %2$s';\n    const combinedTpl = L.local_time_combined || 'Departure & Arrival Port Local Time: %s';\n    if (depTime === arrTime) {\n      localTimesEl.textContent = combinedTpl.replace('%s', depTime);\n      return;\n    }\n    localTimesEl.textContent = pairTpl.replace('%1$s', depTime).replace('%2$s', arrTime);\n  }\n\n  function setStatusInline(html){\n    let box = wrap.querySelector('.tp-controls-status');\n    if (!box) {\n      box = document.createElement('div');\n      box.className = 'tp-controls-status';\n      wrap.querySelector('.tp-controls').appendChild(box);\n    }\n    box.innerHTML = html || '';\n  }\n\n  function clearStatusInline(){\n    const box = wrap.querySelector('.tp-controls-status');\n    if (box) box.innerHTML = '';\n  }\n\n  function navHtml(){\n    if (daySetup !== 'modern') return '';\n    const base = new Date(dateEl.value || new Date());\n    const today = new Date();\n    const tomorrow = new Date(today);\n    tomorrow.setDate(today.getDate() + 1);\n    const isToday = base.toDateString() === today.toDateString();\n    let prevLabel = 'Show previous day';\n    let nextLabel = 'Show next day';\n    if (isToday) nextLabel = 'Show tomorrow';\n    if (base.toDateString() === tomorrow.toDateString()) prevLabel = 'Show today';\n    return '<div class=\"tp-day-nav\">'\n      + (isToday ? '' : '<a href=\"#\" data-shift=\"-1\">'+prevLabel+'<\/a>')\n      + '<a href=\"#\" data-shift=\"1\">'+nextLabel+'<\/a>'\n      + '<\/div>';\n  }\n\n  function shiftDate(delta){\n    const base = new Date(dateEl.value || new Date());\n    base.setDate(base.getDate() + delta);\n    dateEl.value = iso(base);\n    load();\n  }\n\n  function moreButtonHtml(hiddenCount){\n    if (!limit10 || revealAll || hiddenCount <= 0) return '';\n    return '<div style=\"margin-top:12px;text-align:center\">'\n      + '<button type=\"button\" class=\"tp-btn tp-more-btn\">Show more departures ('+hiddenCount+')<\/button>'\n      + '<\/div>';\n  }\n\n  function opCell(r){\n    const name = r.opName || ('Operator '+(r.opId||''));\n    const logo = (cfg.showLogo && r.opLogo) ? '<img decoding=\"async\" src=\"'+r.opLogo+'\" alt=\"'+name+'\"> ' : '';\n    const label = logo + '<span>'+name+'<\/span>';\n    return r.opLink ? '<a class=\"tp-op\" href=\"'+r.opLink+'\" target=\"_blank\" rel=\"nofollow noopener\">'+label+'<\/a>' : '<span class=\"tp-op\">'+label+'<\/span>';\n  }\n\n  function shipInfo(original){\n    const key = (original||'').toLowerCase().trim();\n    const label = (cfg.shipMap && cfg.shipMap[key]) || original || '';\n    const href  = (cfg.shipLinks && cfg.shipLinks[key]) || '';\n    return {label, href};\n  }\n\n  function shipBadge(original){\n    const s = shipInfo(original);\n    const badge = '<span class=\"tp-badge\">'+(s.label||'')+'<\/span>';\n    return s.href ? ('<a href=\"'+s.href+'\" target=\"_blank\" rel=\"nofollow noopener\">'+badge+'<\/a>') : badge;\n  }\n\n  function rowKey(r){\n    return [String(r.routeId||''), String(r.opId||''), String(+r.dep||0), String(+r.arr||0)].join('|');\n  }\n\n  function pickFeaturedKey(rows, avoidKey){\n    if (!activityBoosters || !rows.length) return '';\n    const routesSum = Array.isArray(cfg.routes) ? cfg.routes.reduce((acc, n)=>acc + (parseInt(n,10)||0), 0) : 0;\n    const dateSeed = parseInt(String(dateEl.value || '').replace(\/-\/g,''), 10) || 0;\n\n    let pool = rows;\n    if (avoidKey) {\n      const soonIdx = rows.findIndex(r => rowKey(r) === avoidKey);\n      if (soonIdx >= 0) {\n        const later = rows.slice(soonIdx + 1).filter(r => rowKey(r) !== avoidKey);\n        pool = later.length ? later : rows.filter(r => rowKey(r) !== avoidKey);\n      } else {\n        pool = rows.filter(r => rowKey(r) !== avoidKey);\n      }\n    }\n\n    if (!pool.length) return '';\n    const idx = Math.abs((routesSum + dateSeed + rows.length) % pool.length);\n    return rowKey(pool[idx]);\n  }\n\n  function pickSoonKey(rows){\n    if (!activityBoosters || !rows.length) return '';\n    const now = Date.now();\n    const todayStr = new Date(now).toDateString();\n    const firstUpcoming = rows.find(r => (r.dep instanceof Date) && r.dep.getTime() > now && r.dep.toDateString() === todayStr);\n    return firstUpcoming ? rowKey(firstUpcoming) : '';\n  }\n\n  function boostersFor(r){\n    if (!activityBoosters) return [];\n    const now = Date.now();\n    const depMs = (r.dep instanceof Date) ? r.dep.getTime() : 0;\n    const arrMs = (r.arr instanceof Date) ? r.arr.getTime() : 0;\n    const depIsToday = (r.dep instanceof Date) && (new Date(now).toDateString() === r.dep.toDateString());\n\n    if (depMs > 0 && arrMs > 0 && depMs <= now && arrMs > now) {\n      return [{cls:'tp-booster-sailing', text:(L.tag_currently_sailing || '\ud83d\udfe2 Currently sailing')}];\n    }\n    if (depIsToday && arrMs > 0 && arrMs <= now) {\n      return [{cls:'tp-booster-arrived', text:(L.tag_arrived || '\u26ab Arrived')}];\n    }\n    if (rowKey(r) === soonKey) return [{cls:'tp-booster-soon', text:(L.tag_leaving_soon || '\ud83d\udd34 Leaving soon')}];\n    if (r.dep && r.arr && r.dep.toDateString() !== r.arr.toDateString()) return [{cls:'tp-booster-overnight', text:(L.tag_overnight || 'Overnight')}];\n\n    return [];\n  }\n\n  function boostersHtml(r, floating){\n    const tags = boostersFor(r);\n    if (!tags.length) return '';\n    const cls = floating ? 'tp-boosters tp-boosters-float' : 'tp-boosters';\n    return '<div class=\"'+cls+'\">' + tags.map(t => '<span class=\"tp-booster '+t.cls+'\">'+boosterLabelHtml(t.text)+'<\/span>').join('') + '<\/div>';\n  }\n\n  function boosterLabelHtml(text){\n    return String(text || '').replace(\/^(\ud83d\udd34|\ud83d\udfe2|\u26ab|\u2b50)\\s*\/, '<span class=\"tp-booster-emoji\">$1<\/span>');\n  }\n\n  function routeTicketHtml(label){\n    const raw = String(label || '').trim();\n    if (!raw) return '';\n    if (cfg.theme !== 'small_cards') return escAttr(raw);\n    const parts = raw.split(\/\\s*[\\-\u2013]\\s*\/, 2);\n    if (parts.length < 2) return escAttr(raw);\n    return escAttr(parts[0]) + '<br>' + escAttr(parts[1]);\n  }\n\n  function escAttr(v){\n    return String(v||'').replace(\/[&<>\"']\/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[m]));\n  }\n\n  function bindRowLinks(){\n    if (cfg.theme !== 'max_clickouts') return;\n    out.querySelectorAll('tr.tp-row-link').forEach((row)=>{\n      row.setAttribute('role','link');\n      row.setAttribute('tabindex','0');\n      row.addEventListener('click', (e)=>{\n        if (e.target && e.target.closest('a')) return;\n        const href = row.getAttribute('data-row-link');\n        if (!href) return;\n        window.open(href, '_blank', 'noopener,noreferrer');\n      });\n      row.addEventListener('keydown', (e)=>{\n        if (e.key !== 'Enter' && e.key !== ' ') return;\n        e.preventDefault();\n        row.click();\n      });\n    });\n  }\n\n  function routeLabel(rid){\n    return (cfg.routeLabels && (cfg.routeLabels[String(rid)] || cfg.routeLabels[rid])) || String(rid);\n  }\n\n  function renderTable(rows){\n    const th = {date:L.th_date,dep:L.th_dep,arr:L.th_arr,dur:L.th_dur,ship:L.th_ship,route:L.th_route,op:L.th_op};\n\n    const headCells = [\n      '<th>'+th.date+'<\/th>',\n      '<th>'+th.dep+'<\/th>',\n      '<th>'+th.arr+'<\/th>',\n      '<th>'+th.dur+'<\/th>'\n    ];\n    if (showShip) {\n      headCells.push('<th>'+th.ship+'<\/th>');\n    }\n    headCells.push('<th>'+th.route+'<\/th>');\n    headCells.push('<th>'+th.op+'<\/th>');\n\n    let html = '<table class=\"tp-table\"><thead><tr>'+headCells.join('')+'<\/tr><\/thead><tbody>';\n    rows.forEach(r=>{\n      const dow = WD[r.dep.getDay()] || '';\n      const clickable = (cfg.theme === 'max_clickouts' && r.opLink);\n      const rowAttr = clickable ? (' class=\"tp-row-link\" data-row-link=\"'+escAttr(r.opLink)+'\"') : '';\n      const dateBoosters = (cfg.theme === 'max_clickouts') ? '' : boostersHtml(r, false);\n      const routeBoosters = (cfg.theme === 'max_clickouts') ? boostersHtml(r, false) : '';\n      const cells = [\n        '<td>'+dShort(r.dep)+' <span class=\"tp-day\">'+dow+'<\/span>'+dateBoosters+'<\/td>',\n        '<td>'+hhmm(r.dep)+'<\/td>',\n        '<td>'+hhmm(r.arr)+'<\/td>',\n        '<td>'+dur(r.min)+'<\/td>'\n      ];\n      if (showShip) {\n        cells.push('<td>'+shipBadge(r.ship)+'<\/td>');\n      }\n      cells.push('<td>'+routeBoosters+'<div>'+routeLabel(r.routeId)+'<\/div><\/td>');\n      cells.push('<td>'+opCell(r)+'<\/td>');\n      html += '<tr'+rowAttr+'>'+cells.join('')+'<\/tr>';\n    });\n    html += '<\/tbody><\/table>';\n    return html;\n  }\n\n  function renderCards(rows){\n    if (cfg.theme === 'extended_cards') {\n      return renderExtendedCards(rows);\n    }\n\n    let html = '<div class=\"tp-cardlist\">';\n    rows.forEach(r=>{\n      const dow   = WD[r.dep.getDay()] || '';\n      const times = hhmm(r.dep) + ' <span class=\"tp-arrow\">\u2192<\/span> ' + hhmm(r.arr);\n      html += '<div class=\"tp-card\">'+boostersHtml(r, true)+\n        \/\/ Row 1: Date + weekday + times\n        '<div class=\"tp-mrow\">'+\n          '<div class=\"lhs\"><span class=\"tp-sub\">'+dShort(r.dep)+'<\/span><span class=\"tp-day\">'+dow+'<\/span><\/div>'+\n          '<div class=\"rhs\"><span class=\"tp-time\">'+times+'<\/span><\/div>'+\n        '<\/div>';\n\n      if (showShip) {\n        html +=\n        \/\/ Row 2: Ship\n        '<div class=\"tp-mrow\">'+\n          '<div class=\"lhs\">'+shipBadge(r.ship)+'<\/div>'+\n          '<div class=\"rhs\"><\/div>'+\n        '<\/div>';\n      }\n\n      html +=\n        \/\/ Row 3: Operator + route label\n        '<div class=\"tp-mrow\">'+\n          '<div class=\"lhs\">'+opCell(r)+'<\/div>'+\n          '<div class=\"rhs\"><span class=\"tp-route-ticket\">'+routeTicketHtml(routeLabel(r.routeId))+'<\/span><\/div>'+\n        '<\/div>'+\n      '<\/div>';\n    });\n    html += '<\/div>';\n    return html;\n  }\n\n  function renderExtendedCards(rows){\n    const ctaText = L.more_link || 'More sailings';\n    let html = '<div class=\"tp-cardlist\">';\n    rows.forEach(r=>{\n      const dow = WD[r.dep.getDay()] || '';\n      const link = r.opLink ? ('<a class=\"tp-ext-cta\" href=\"'+r.opLink+'\" target=\"_blank\" rel=\"nofollow noopener\">'+ctaText+' <span class=\"tp-ext-cta-arrow\">\u00bb<\/span><\/a>') : '<span class=\"tp-ext-cta\">'+ctaText+' <span class=\"tp-ext-cta-arrow\">\u00bb<\/span><\/span>';\n      html += '<div class=\"tp-ext-card\">'\n        + boostersHtml(r, true)\n        + '<div class=\"tp-ext-left\">'\n        +   '<div>'+opCell(r)+'<\/div>'\n        +   (showShip ? ('<div>'+shipBadge(r.ship)+'<\/div>') : '')\n        + '<\/div>'\n        + '<div class=\"tp-ext-mid\">'\n        +   '<div class=\"tp-ext-top\"><span class=\"tp-sub\">'+dShort(r.dep)+' <span class=\"tp-day\">'+dow+'<\/span><\/span><span class=\"tp-ext-dur\">'+dur(r.min)+'<\/span><\/div>'\n        +   '<div class=\"tp-ext-times\"><span class=\"tp-ext-time\">'+hhmm(r.dep)+'<\/span><span class=\"tp-ext-line\"><\/span><span class=\"tp-ext-dur\">'+dur(r.min)+'<\/span><span class=\"tp-ext-line\"><\/span><span class=\"tp-ext-time\">'+hhmm(r.arr)+'<\/span><\/div>'\n        +   '<div class=\"tp-ext-route\">'+routeLabel(r.routeId)+'<\/div>'\n        + '<\/div>'\n        + '<div class=\"tp-ext-right\">'+link+'<\/div>'\n        + '<\/div>';\n    });\n    html += '<\/div>';\n    return html;\n  }\n\n  \/\/ Safe fetch helper\n  async function fetchOne(routeId, from, to){\n    try{\n      const url = new URL(cfg.rest);\n      url.searchParams.set('route', String(routeId));\n      url.searchParams.set('from', from);\n      url.searchParams.set('to', to);\n      const res = await fetch(url.toString(), {credentials:'same-origin'});\n      if(!res.ok) return { rid: routeId, error: 'HTTP '+res.status };\n      const json = await res.json();\n      const list = (json && json.data && Array.isArray(json.data.rows)) ? json.data.rows : [];\n      return { rid: routeId, rows: list };\n    }catch(e){\n      return { rid: routeId, error: String(e && e.message ? e.message : e) };\n    }\n  }\n\n  async function load(){\n    btn.disabled = true; setLoader(true); status.textContent = L.status_loading;\n\n    const start = new Date(dateEl.value || new Date());\n    const days  = (daySetup === 'modern') ? 1 : Math.max(1, parseInt(daysEl.value,10)||1);\n    const end   = new Date(start); end.setDate(start.getDate()+days-1);\n    const fromIso = iso(start), toIso = iso(end);\n\n    try{\n      const results  = await Promise.all((cfg.routes||[]).map(rid=>fetchOne(rid, fromIso, toIso)));\n      const oks  = results.filter(r => !r.error);\n      const errs = results.filter(r =>  r.error);\n\n      const all = [];\n      oks.forEach(({rid, rows})=>{\n        rows.forEach(t=>{\n          all.push({\n            routeId: rid,\n            opId: t.operatorId || null,\n            opName: t.operatorName || '',\n            opLogo: t.operatorLogo || '',\n            opLink: t.operatorLink || '',\n            dep: toLocal(t.departureTime),\n            arr: toLocal(t.arrivalTime),\n            min: t.durationInMinutes || 0,\n            ship: t.shipName || ''\n          });\n        });\n      });\n\n      let rows = all;\n      if (Array.isArray(cfg.opFilter) && cfg.opFilter.length>0) {\n        rows = all.filter(r => r.opId && cfg.opFilter.includes(String(r.opId)));\n      }\n\n      rows.sort((a,b)=> a.dep - b.dep);\n\n      const todayStr = new Date().toDateString();\n      const nowMs = Date.now();\n      const arrivedToday = rows\n        .filter(r => (r.dep instanceof Date) && (r.arr instanceof Date) && r.dep.toDateString() === todayStr && r.arr.getTime() <= nowMs)\n        .sort((a,b) => b.arr - a.arr);\n      if (arrivedToday.length > 2) {\n        const keep = new Set(arrivedToday.slice(0,2).map(rowKey));\n        rows = rows.filter(r => {\n          const isArrivedToday = (r.dep instanceof Date) && (r.arr instanceof Date) && r.dep.toDateString() === todayStr && r.arr.getTime() <= nowMs;\n          return !isArrivedToday || keep.has(rowKey(r));\n        });\n      }\n\n      const total = rows.length;\n      if (total===0){\n        status.textContent = L.status_empty + (errs.length ? ' (some routes returned no data or errors)' : '');\n        clearStatusInline();\n        out.innerHTML='';\n        setLoader(false); btn.disabled=false; return;\n      }\n\n      const visibleRows = (limit10 && !revealAll && rows.length > 10) ? rows.slice(0,10) : rows;\n      const hiddenCount = rows.length - visibleRows.length;\n      soonKey = pickSoonKey(visibleRows);\n      featuredKey = pickFeaturedKey(visibleRows, soonKey);\n\n      status.textContent = errs.length ? ('Skipped '+errs.length+' route'+(errs.length>1?'s':'')) : '';\n      setStatusInline('<strong>'+total+'<\/strong> sailings found');\n      out.innerHTML = renderTable(visibleRows) + renderCards(visibleRows) + moreButtonHtml(hiddenCount);\n      bindRowLinks();\n      if (navHost) navHost.innerHTML = navHtml();\n\n    } catch(e){\n      status.textContent = 'Failed to load data';\n      out.innerHTML = '<pre>'+String(e.message||e)+'<\/pre>';\n    } finally {\n      setLoader(false);\n      btn.disabled = false;\n    }\n  }\n\n  wrap.addEventListener('click', function(e){\n    const nav = e.target.closest('.tp-day-nav a[data-shift]');\n    if (nav) {\n      e.preventDefault();\n      shiftDate(parseInt(nav.getAttribute('data-shift'),10) || 0);\n      return;\n    }\n    const more = e.target.closest('.tp-more-btn');\n    if (more) {\n      e.preventDefault();\n      revealAll = true;\n      load();\n    }\n  });\n\n  btn.addEventListener('click', load);\n  if (navHost) navHost.innerHTML = navHtml();\n  renderLocalTimes();\n  setInterval(renderLocalTimes, 30000);\n  if (cfg.autoload) load();\n})();\n<\/script>\n\n\n\n    <div class=\"fh-drive-tool\">\r\n        <h3>Beregn hvorn\u00e5r du ankommer til Hirtshals havn fra Danmark<\/h3>\r\n        <p class=\"fh-text\">\r\n            Det kan v\u00e6re smart at bruge v\u00e6rkt\u00f8jet nedenfor. V\u00e6lg blot hvor du k\u00f8rer fra, hvilken dag og hvilket tidspunkt, og se hvorn\u00e5r du cirka ankommer til Hirtshals havn. P\u00e5 den m\u00e5de kan du v\u00e6lge en f\u00e6rge, der passer godt til din rejseplan.\r\n        <\/p>\r\n\r\n        <div class=\"fh-field\">\r\n            <label for=\"fh_place\">Afgangsby<\/label>\r\n            <select id=\"fh_place\">\r\n                <option value=\"aalborg\">Aalborg<\/option>\r\n                <option value=\"aarhus\">Aarhus<\/option>\r\n                <option value=\"randers\">Randers<\/option>\r\n                <option value=\"viborg\">Viborg<\/option>\r\n                <option value=\"herning\">Herning<\/option>\r\n                <option value=\"odense\">Odense<\/option>\r\n                <option value=\"esbjerg\">Esbjerg<\/option>\r\n                <option value=\"copenhagen\" selected>K\u00f8benhavn<\/option>\r\n            <\/select>\r\n        <\/div>\r\n\r\n        <div class=\"fh-row\">\r\n            <div class=\"fh-field\">\r\n                <label for=\"fh_day\">Afrejsedag<\/label>\r\n                <select id=\"fh_day\">\r\n                    <option value=\"1\">Mandag<\/option>\r\n                    <option value=\"2\">Tirsdag<\/option>\r\n                    <option value=\"3\">Onsdag<\/option>\r\n                    <option value=\"4\">Torsdag<\/option>\r\n                    <option value=\"5\">Fredag<\/option>\r\n                    <option value=\"6\">L\u00f8rdag<\/option>\r\n                    <option value=\"0\">S\u00f8ndag<\/option>\r\n                <\/select>\r\n            <\/div>\r\n\r\n            <div class=\"fh-field\">\r\n                <label for=\"fh_time\">Afgangstid<\/label>\r\n                <input type=\"time\" id=\"fh_time\" value=\"06:30\">\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <div class=\"fh-field\">\r\n            <button type=\"button\" id=\"fh_calc\">Beregn ankomsttid<\/button>\r\n        <\/div>\r\n\r\n        <div id=\"fh_result\"><\/div>\r\n    <\/div>\r\n\r\n    <style>\r\n    .fh-drive-tool {\r\n        background: #f0f5fa;\r\n        padding: 26px 28px;\r\n        border-radius: 10px;\r\n    }\r\n\r\n    .fh-drive-tool h3 {\r\n        margin-bottom: 10px;\r\n    }\r\n\r\n    .fh-drive-tool .fh-text {\r\n        font-size: 0.95em;\r\n        margin-bottom: 20px;\r\n        line-height: 1.6;\r\n    }\r\n\r\n    .fh-drive-tool .fh-field {\r\n        margin-bottom: 18px;\r\n    }\r\n\r\n    .fh-drive-tool .fh-row {\r\n        display: flex;\r\n        gap: 14px;\r\n        align-items: flex-end;\r\n    }\r\n\r\n    .fh-drive-tool .fh-row .fh-field {\r\n        width: 50%;\r\n        display: flex;\r\n        flex-direction: column;\r\n    }\r\n\r\n    .fh-drive-tool button {\r\n        width: 100%;\r\n        padding: 16px 0;\r\n    }\r\n\r\n    .fh-drive-tool #fh_result {\r\n        margin-top: 20px;\r\n        font-size: 0.95em;\r\n        line-height: 1.6;\r\n    }\r\n\r\n    .fh-drive-tool #fh_result strong {\r\n        font-size: 1.05em;\r\n    }\r\n\r\n    @media (max-width: 767px) {\r\n        .fh-drive-tool .fh-row {\r\n            display: block;\r\n        }\r\n\r\n        .fh-drive-tool .fh-row .fh-field {\r\n            width: 100%;\r\n        }\r\n    }\r\n    <\/style>\r\n\r\n    <script>\r\n    document.addEventListener('DOMContentLoaded', function () {\r\n        var placeEl = document.getElementById('fh_place');\r\n        var dayEl = document.getElementById('fh_day');\r\n        var timeEl = document.getElementById('fh_time');\r\n        var calcEl = document.getElementById('fh_calc');\r\n        var resultEl = document.getElementById('fh_result');\r\n\r\n        if (!placeEl) return;\r\n\r\n        var baseMinutes = {\r\n            aalborg: 45,\r\n            aarhus: 115,\r\n            randers: 95,\r\n            viborg: 80,\r\n            herning: 105,\r\n            odense: 210,\r\n            esbjerg: 165,\r\n            copenhagen: 290\r\n        };\r\n\r\n        var labels = {\r\n            aalborg: 'Aalborg',\r\n            aarhus: 'Aarhus',\r\n            randers: 'Randers',\r\n            viborg: 'Viborg',\r\n            herning: 'Herning',\r\n            odense: 'Odense',\r\n            esbjerg: 'Esbjerg',\r\n            copenhagen: 'K\u00f8benhavn'\r\n        };\r\n\r\n        function pad(n){ return String(n).padStart(2,'0'); }\r\n\r\n        function minutesToText(total){\r\n            var h = Math.floor(total \/ 60);\r\n            var m = total % 60;\r\n            return m === 0 ? h + ' timer' : h + ' timer og ' + m + ' minutter';\r\n        }\r\n\r\n        function getTrafficExtra(day, minutes, place){\r\n            var extra = 0;\r\n            var isWeekday = day >= 1 && day <= 5;\r\n\r\n            if (isWeekday){\r\n                if (minutes >= 360 && minutes < 570) extra += 20;\r\n                if (minutes >= 930 && minutes < 1110) extra += 15;\r\n            }\r\n\r\n            if (['aarhus','odense','copenhagen'].includes(place) && isWeekday && minutes < 600){\r\n                extra += 10;\r\n            }\r\n\r\n            return Math.max(0, extra);\r\n        }\r\n\r\n        function formatArrival(time, drive){\r\n            var parts = time.split(':');\r\n            var total = (parseInt(parts[0])*60 + parseInt(parts[1]) + drive) % 1440;\r\n            return pad(Math.floor(total\/60)) + ':' + pad(total%60);\r\n        }\r\n\r\n        calcEl.addEventListener('click', function(){\r\n            var place = placeEl.value;\r\n            var day = parseInt(dayEl.value,10);\r\n            var time = timeEl.value;\r\n\r\n            if (!time){\r\n                resultEl.innerHTML = 'V\u00e6lg f\u00f8rst et afgangstidspunkt.';\r\n                return;\r\n            }\r\n\r\n            var minutes = (parseInt(time.split(':')[0])*60 + parseInt(time.split(':')[1]));\r\n            var total = baseMinutes[place] + getTrafficExtra(day, minutes, place);\r\n            var arrival = formatArrival(time, total);\r\n\r\n            resultEl.innerHTML =\r\n                '<p><strong>Forventet ankomsttid til Hirtshals havn: ' + arrival + '<\/strong><br>' +\r\n                'Rejsetiden er cirka ' + minutesToText(total) + '. Denne beregning tager h\u00f8jde for trafik, myldretid, hverdage og eventuelle forsinkelser.<\/p>';\r\n        });\r\n    });\r\n    <\/script>\r\n    \n\n\n\n<h2 class=\"wp-block-heading\">Kristiansand Hirtshals afgangs- og ankomsttider<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Rejser du ogs\u00e5 tilbage fra Kristiansand til Hirtshals? S\u00e5 tjek afgangs- og ankomsttiderne fra <a href=\"https:\/\/hirtshals-kristiansand.com\/da\/kristiansand-havn\/\">Kristiansand havn<\/a> nedenfor.<\/p>\n\n\n<div class=\"tp-wrap tp-theme-extended_cards tp-day-setup-modern\" data-config=\"{&quot;rest&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/wp-json\\\/timetables-pro\\\/v1\\\/timetables&quot;,&quot;routes&quot;:[172],&quot;days&quot;:6,&quot;autoload&quot;:true,&quot;theme&quot;:&quot;extended_cards&quot;,&quot;daySetup&quot;:&quot;modern&quot;,&quot;limit10&quot;:false,&quot;routeLabels&quot;:{&quot;172&quot;:&quot;Kristiansand til Hirtshals&quot;},&quot;opFilter&quot;:[&quot;8&quot;,&quot;27&quot;],&quot;labels&quot;:{&quot;ui_date&quot;:&quot;Dato&quot;,&quot;ui_days&quot;:&quot;Dage&quot;,&quot;ui_button&quot;:&quot;Vis afgange&quot;,&quot;status_idle&quot;:&quot;V\\u00e6lg en dato og klik p\\u00e5 Vis afgange&quot;,&quot;status_loading&quot;:&quot;Indl\\u00e6ser...&quot;,&quot;status_empty&quot;:&quot;Ingen afgange fundet&quot;,&quot;status_found&quot;:&quot;%d afgange fundet&quot;,&quot;th_date&quot;:&quot;Dato&quot;,&quot;th_dep&quot;:&quot;Afgang&quot;,&quot;th_arr&quot;:&quot;Ankomst&quot;,&quot;th_dur&quot;:&quot;Varighed&quot;,&quot;th_ship&quot;:&quot;Skib&quot;,&quot;th_op&quot;:&quot;Operat\\u00f8r&quot;,&quot;th_route&quot;:&quot;Rute&quot;,&quot;wd_sun&quot;:&quot;S\\u00d8N&quot;,&quot;wd_mon&quot;:&quot;MAN&quot;,&quot;wd_tue&quot;:&quot;TIR&quot;,&quot;wd_wed&quot;:&quot;ONS&quot;,&quot;wd_thu&quot;:&quot;TOR&quot;,&quot;wd_fri&quot;:&quot;FRE&quot;,&quot;wd_sat&quot;:&quot;L\\u00d8R&quot;,&quot;summary_footer_one&quot;:&quot;\\u00bb Se alle %s afgange i vores sejlplan&quot;,&quot;summary_footer_two&quot;:&quot;\\u00bb Se flere afgange for %1$s og %2$s i vores sejlplan&quot;,&quot;summary_footer_generic&quot;:&quot;Se vores sejlplan for flere afgange&quot;,&quot;summary_header&quot;:&quot;De n\\u00e6ste to afgange:&quot;,&quot;summary_no_upcoming&quot;:&quot;Ingen kommende afgange fundet&quot;,&quot;more_link&quot;:&quot;Flere afgange&quot;,&quot;local_time_pair&quot;:&quot;Lokal tid afgangshavn: %1$s \\\/ Lokal tid ankomsthavn: %2$s&quot;,&quot;local_time_combined&quot;:&quot;Lokal tid afgangs- og ankomsthavn: %s&quot;,&quot;tag_often_booked&quot;:&quot;\\u2b50 Ofte booket&quot;,&quot;tag_leaving_soon&quot;:&quot;\\ud83d\\udd34 Afg\\u00e5r snart&quot;,&quot;tag_currently_sailing&quot;:&quot;\\ud83d\\udfe2 Sejler nu&quot;,&quot;tag_arrived&quot;:&quot;\\u26ab Ankommet&quot;,&quot;tag_overnight&quot;:&quot;Over natten&quot;},&quot;showLogo&quot;:true,&quot;activityBoosters&quot;:true,&quot;title&quot;:&quot;Alle overfarter&quot;,&quot;shipMap&quot;:[],&quot;shipLinks&quot;:{&quot;bf&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/ms-bergensfjord\\\/&quot;,&quot;superspeed 1&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/color-line-superspeed-1\\\/&quot;},&quot;showShip&quot;:true,&quot;departurePort&quot;:&quot;Kristiansand&quot;,&quot;arrivalPort&quot;:&quot;Hirtshals&quot;,&quot;departureTimezone&quot;:&quot;Europe\\\/Oslo&quot;,&quot;arrivalTimezone&quot;:&quot;Europe\\\/Copenhagen&quot;}\">\n  <div class=\"tp-controls\" role=\"group\" aria-label=\"Timetable controls\">\n    <div>\n      <label for=\"tp-date-ops\">Dato<\/label><br>\n      <input id=\"tp-date-ops\" class=\"tp-date\" type=\"date\" aria-label=\"Dato\">\n    <\/div>\n    <div class=\"tp-day-field\">\n      <label for=\"tp-range-ops\">Dage<\/label><br>\n      <select id=\"tp-range-ops\" class=\"tp-range\" aria-label=\"Dage\">\n        <option value=\"1\">1<\/option><option value=\"3\">3<\/option><option value=\"7\">7<\/option><option value=\"14\">14<\/option>\n      <\/select>\n    <\/div>\n    <div><button id=\"tp-load-ops\" class=\"tp-btn\">Vis afgange<\/button><\/div>\n  <\/div>\n\n  <div class=\"tp-local-nav-row\">\n    <div class=\"tp-local-times\" id=\"tp-local-times-ops\" aria-live=\"polite\"><\/div>\n    <div class=\"tp-day-nav-host\"><\/div>\n  <\/div>\n\n      <h3 style=\"margin:8px 0 12px;color:#0f172a;\">Alle overfarter<\/h3>\n  \n  <div id=\"tp-status-ops\" class=\"tp-muted\" aria-live=\"polite\">V\u00e6lg en dato og klik p\u00e5 Vis afgange<\/div>\n\n  <div id=\"tp-ops-results\"><\/div>\n\n  <div class=\"tp-loader\" aria-hidden=\"true\">\n    <div class=\"tp-loader-card\">\n      <div class=\"tp-spinner\" aria-hidden=\"true\"><\/div>\n      <div class=\"tp-loader-text\">Indl\u00e6ser...<\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function(){\n  const wrap   = document.currentScript.previousElementSibling;\n  const cfg    = JSON.parse(wrap.getAttribute('data-config')||'{}');\n  const L      = cfg.labels||{};\n  const dateEl = wrap.querySelector('#tp-date-ops');\n  const daysEl = wrap.querySelector('#tp-range-ops');\n  const btn    = wrap.querySelector('#tp-load-ops');\n  const status = wrap.querySelector('#tp-status-ops');\n  const out    = wrap.querySelector('#tp-ops-results');\n  const loader = wrap.querySelector('.tp-loader');\n  const navHost = wrap.querySelector('.tp-day-nav-host');\n  const localTimesEl = wrap.querySelector('#tp-local-times-ops');\n\n  const showShip = (cfg.showShip !== false && cfg.showShip !== 0 && cfg.showShip !== '0');\n  const activityBoosters = !!cfg.activityBoosters;\n  const daySetup = (cfg.daySetup === 'modern') ? 'modern' : 'classic';\n  const limit10 = !!cfg.limit10;\n  let featuredKey = '';\n  let soonKey = '';\n  let revealAll = false;\n\n  if (daySetup === 'modern') {\n    const dayField = wrap.querySelector('.tp-day-field');\n    if (dayField) dayField.style.display = 'none';\n  }\n\n  dateEl.valueAsDate = new Date();\n  dateEl.min = new Date().toISOString().split('T')[0];\n  Array.from(daysEl.options).forEach(o=>{ if(parseInt(o.value,10)===parseInt(cfg.days||7,10)) o.selected=true; });\n\n  function pad(n){ return String(n).padStart(2,'0'); }\n  function iso(d){ return d.getFullYear()+'-'+pad(d.getMonth()+1)+'-'+pad(d.getDate()); }\n  function toLocal(s){ return new Date(s); }\n  function hhmm(d){ return d.toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'}); }\n  function dShort(d){ return d.toLocaleDateString([], {day:'numeric', month:'short'}); }\n  function dur(m){ const h=Math.floor(m\/60), r=m%60; return r? (h+'h '+r+'m') : (h+'h'); }\n\n  const WD = [\n    L.wd_sun || 'SUN',\n    L.wd_mon || 'MON',\n    L.wd_tue || 'TUE',\n    L.wd_wed || 'WED',\n    L.wd_thu || 'THU',\n    L.wd_fri || 'FRI',\n    L.wd_sat || 'SAT'\n  ];\n\n  function setLoader(v){ loader.classList.toggle('show', !!v); loader.setAttribute('aria-hidden', v?'false':'true'); }\n\n  function formatZoneTime(tz){\n    if (!tz) return '--:--';\n    try {\n      return new Intl.DateTimeFormat([], {hour:'2-digit', minute:'2-digit', hour12:false, timeZone: tz}).format(new Date());\n    } catch (e) {\n      return '--:--';\n    }\n  }\n\n  function renderLocalTimes(){\n    if (!localTimesEl) return;\n    if (!cfg.departureTimezone || !cfg.arrivalTimezone) {\n      localTimesEl.textContent = '';\n      return;\n    }\n    const depTime = formatZoneTime(cfg.departureTimezone);\n    const arrTime = formatZoneTime(cfg.arrivalTimezone);\n    const pairTpl = L.local_time_pair || 'Departure Port Local Time: %1$s \/ Arrival Port Local Time: %2$s';\n    const combinedTpl = L.local_time_combined || 'Departure & Arrival Port Local Time: %s';\n    if (depTime === arrTime) {\n      localTimesEl.textContent = combinedTpl.replace('%s', depTime);\n      return;\n    }\n    localTimesEl.textContent = pairTpl.replace('%1$s', depTime).replace('%2$s', arrTime);\n  }\n\n  function setStatusInline(html){\n    let box = wrap.querySelector('.tp-controls-status');\n    if (!box) {\n      box = document.createElement('div');\n      box.className = 'tp-controls-status';\n      wrap.querySelector('.tp-controls').appendChild(box);\n    }\n    box.innerHTML = html || '';\n  }\n\n  function clearStatusInline(){\n    const box = wrap.querySelector('.tp-controls-status');\n    if (box) box.innerHTML = '';\n  }\n\n  function navHtml(){\n    if (daySetup !== 'modern') return '';\n    const base = new Date(dateEl.value || new Date());\n    const today = new Date();\n    const tomorrow = new Date(today);\n    tomorrow.setDate(today.getDate() + 1);\n    const isToday = base.toDateString() === today.toDateString();\n    let prevLabel = 'Show previous day';\n    let nextLabel = 'Show next day';\n    if (isToday) nextLabel = 'Show tomorrow';\n    if (base.toDateString() === tomorrow.toDateString()) prevLabel = 'Show today';\n    return '<div class=\"tp-day-nav\">'\n      + (isToday ? '' : '<a href=\"#\" data-shift=\"-1\">'+prevLabel+'<\/a>')\n      + '<a href=\"#\" data-shift=\"1\">'+nextLabel+'<\/a>'\n      + '<\/div>';\n  }\n\n  function shiftDate(delta){\n    const base = new Date(dateEl.value || new Date());\n    base.setDate(base.getDate() + delta);\n    dateEl.value = iso(base);\n    load();\n  }\n\n  function moreButtonHtml(hiddenCount){\n    if (!limit10 || revealAll || hiddenCount <= 0) return '';\n    return '<div style=\"margin-top:12px;text-align:center\">'\n      + '<button type=\"button\" class=\"tp-btn tp-more-btn\">Show more departures ('+hiddenCount+')<\/button>'\n      + '<\/div>';\n  }\n\n  function opCell(r){\n    const name = r.opName || ('Operator '+(r.opId||''));\n    const logo = (cfg.showLogo && r.opLogo) ? '<img decoding=\"async\" src=\"'+r.opLogo+'\" alt=\"'+name+'\"> ' : '';\n    const label = logo + '<span>'+name+'<\/span>';\n    return r.opLink ? '<a class=\"tp-op\" href=\"'+r.opLink+'\" target=\"_blank\" rel=\"nofollow noopener\">'+label+'<\/a>' : '<span class=\"tp-op\">'+label+'<\/span>';\n  }\n\n  function shipInfo(original){\n    const key = (original||'').toLowerCase().trim();\n    const label = (cfg.shipMap && cfg.shipMap[key]) || original || '';\n    const href  = (cfg.shipLinks && cfg.shipLinks[key]) || '';\n    return {label, href};\n  }\n\n  function shipBadge(original){\n    const s = shipInfo(original);\n    const badge = '<span class=\"tp-badge\">'+(s.label||'')+'<\/span>';\n    return s.href ? ('<a href=\"'+s.href+'\" target=\"_blank\" rel=\"nofollow noopener\">'+badge+'<\/a>') : badge;\n  }\n\n  function rowKey(r){\n    return [String(r.routeId||''), String(r.opId||''), String(+r.dep||0), String(+r.arr||0)].join('|');\n  }\n\n  function pickFeaturedKey(rows, avoidKey){\n    if (!activityBoosters || !rows.length) return '';\n    const routesSum = Array.isArray(cfg.routes) ? cfg.routes.reduce((acc, n)=>acc + (parseInt(n,10)||0), 0) : 0;\n    const dateSeed = parseInt(String(dateEl.value || '').replace(\/-\/g,''), 10) || 0;\n\n    let pool = rows;\n    if (avoidKey) {\n      const soonIdx = rows.findIndex(r => rowKey(r) === avoidKey);\n      if (soonIdx >= 0) {\n        const later = rows.slice(soonIdx + 1).filter(r => rowKey(r) !== avoidKey);\n        pool = later.length ? later : rows.filter(r => rowKey(r) !== avoidKey);\n      } else {\n        pool = rows.filter(r => rowKey(r) !== avoidKey);\n      }\n    }\n\n    if (!pool.length) return '';\n    const idx = Math.abs((routesSum + dateSeed + rows.length) % pool.length);\n    return rowKey(pool[idx]);\n  }\n\n  function pickSoonKey(rows){\n    if (!activityBoosters || !rows.length) return '';\n    const now = Date.now();\n    const todayStr = new Date(now).toDateString();\n    const firstUpcoming = rows.find(r => (r.dep instanceof Date) && r.dep.getTime() > now && r.dep.toDateString() === todayStr);\n    return firstUpcoming ? rowKey(firstUpcoming) : '';\n  }\n\n  function boostersFor(r){\n    if (!activityBoosters) return [];\n    const now = Date.now();\n    const depMs = (r.dep instanceof Date) ? r.dep.getTime() : 0;\n    const arrMs = (r.arr instanceof Date) ? r.arr.getTime() : 0;\n    const depIsToday = (r.dep instanceof Date) && (new Date(now).toDateString() === r.dep.toDateString());\n\n    if (depMs > 0 && arrMs > 0 && depMs <= now && arrMs > now) {\n      return [{cls:'tp-booster-sailing', text:(L.tag_currently_sailing || '\ud83d\udfe2 Currently sailing')}];\n    }\n    if (depIsToday && arrMs > 0 && arrMs <= now) {\n      return [{cls:'tp-booster-arrived', text:(L.tag_arrived || '\u26ab Arrived')}];\n    }\n    if (rowKey(r) === soonKey) return [{cls:'tp-booster-soon', text:(L.tag_leaving_soon || '\ud83d\udd34 Leaving soon')}];\n    if (r.dep && r.arr && r.dep.toDateString() !== r.arr.toDateString()) return [{cls:'tp-booster-overnight', text:(L.tag_overnight || 'Overnight')}];\n\n    return [];\n  }\n\n  function boostersHtml(r, floating){\n    const tags = boostersFor(r);\n    if (!tags.length) return '';\n    const cls = floating ? 'tp-boosters tp-boosters-float' : 'tp-boosters';\n    return '<div class=\"'+cls+'\">' + tags.map(t => '<span class=\"tp-booster '+t.cls+'\">'+boosterLabelHtml(t.text)+'<\/span>').join('') + '<\/div>';\n  }\n\n  function boosterLabelHtml(text){\n    return String(text || '').replace(\/^(\ud83d\udd34|\ud83d\udfe2|\u26ab|\u2b50)\\s*\/, '<span class=\"tp-booster-emoji\">$1<\/span>');\n  }\n\n  function routeTicketHtml(label){\n    const raw = String(label || '').trim();\n    if (!raw) return '';\n    if (cfg.theme !== 'small_cards') return escAttr(raw);\n    const parts = raw.split(\/\\s*[\\-\u2013]\\s*\/, 2);\n    if (parts.length < 2) return escAttr(raw);\n    return escAttr(parts[0]) + '<br>' + escAttr(parts[1]);\n  }\n\n  function escAttr(v){\n    return String(v||'').replace(\/[&<>\"']\/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[m]));\n  }\n\n  function bindRowLinks(){\n    if (cfg.theme !== 'max_clickouts') return;\n    out.querySelectorAll('tr.tp-row-link').forEach((row)=>{\n      row.setAttribute('role','link');\n      row.setAttribute('tabindex','0');\n      row.addEventListener('click', (e)=>{\n        if (e.target && e.target.closest('a')) return;\n        const href = row.getAttribute('data-row-link');\n        if (!href) return;\n        window.open(href, '_blank', 'noopener,noreferrer');\n      });\n      row.addEventListener('keydown', (e)=>{\n        if (e.key !== 'Enter' && e.key !== ' ') return;\n        e.preventDefault();\n        row.click();\n      });\n    });\n  }\n\n  function routeLabel(rid){\n    return (cfg.routeLabels && (cfg.routeLabels[String(rid)] || cfg.routeLabels[rid])) || String(rid);\n  }\n\n  function renderTable(rows){\n    const th = {date:L.th_date,dep:L.th_dep,arr:L.th_arr,dur:L.th_dur,ship:L.th_ship,route:L.th_route,op:L.th_op};\n\n    const headCells = [\n      '<th>'+th.date+'<\/th>',\n      '<th>'+th.dep+'<\/th>',\n      '<th>'+th.arr+'<\/th>',\n      '<th>'+th.dur+'<\/th>'\n    ];\n    if (showShip) {\n      headCells.push('<th>'+th.ship+'<\/th>');\n    }\n    headCells.push('<th>'+th.route+'<\/th>');\n    headCells.push('<th>'+th.op+'<\/th>');\n\n    let html = '<table class=\"tp-table\"><thead><tr>'+headCells.join('')+'<\/tr><\/thead><tbody>';\n    rows.forEach(r=>{\n      const dow = WD[r.dep.getDay()] || '';\n      const clickable = (cfg.theme === 'max_clickouts' && r.opLink);\n      const rowAttr = clickable ? (' class=\"tp-row-link\" data-row-link=\"'+escAttr(r.opLink)+'\"') : '';\n      const dateBoosters = (cfg.theme === 'max_clickouts') ? '' : boostersHtml(r, false);\n      const routeBoosters = (cfg.theme === 'max_clickouts') ? boostersHtml(r, false) : '';\n      const cells = [\n        '<td>'+dShort(r.dep)+' <span class=\"tp-day\">'+dow+'<\/span>'+dateBoosters+'<\/td>',\n        '<td>'+hhmm(r.dep)+'<\/td>',\n        '<td>'+hhmm(r.arr)+'<\/td>',\n        '<td>'+dur(r.min)+'<\/td>'\n      ];\n      if (showShip) {\n        cells.push('<td>'+shipBadge(r.ship)+'<\/td>');\n      }\n      cells.push('<td>'+routeBoosters+'<div>'+routeLabel(r.routeId)+'<\/div><\/td>');\n      cells.push('<td>'+opCell(r)+'<\/td>');\n      html += '<tr'+rowAttr+'>'+cells.join('')+'<\/tr>';\n    });\n    html += '<\/tbody><\/table>';\n    return html;\n  }\n\n  function renderCards(rows){\n    if (cfg.theme === 'extended_cards') {\n      return renderExtendedCards(rows);\n    }\n\n    let html = '<div class=\"tp-cardlist\">';\n    rows.forEach(r=>{\n      const dow   = WD[r.dep.getDay()] || '';\n      const times = hhmm(r.dep) + ' <span class=\"tp-arrow\">\u2192<\/span> ' + hhmm(r.arr);\n      html += '<div class=\"tp-card\">'+boostersHtml(r, true)+\n        \/\/ Row 1: Date + weekday + times\n        '<div class=\"tp-mrow\">'+\n          '<div class=\"lhs\"><span class=\"tp-sub\">'+dShort(r.dep)+'<\/span><span class=\"tp-day\">'+dow+'<\/span><\/div>'+\n          '<div class=\"rhs\"><span class=\"tp-time\">'+times+'<\/span><\/div>'+\n        '<\/div>';\n\n      if (showShip) {\n        html +=\n        \/\/ Row 2: Ship\n        '<div class=\"tp-mrow\">'+\n          '<div class=\"lhs\">'+shipBadge(r.ship)+'<\/div>'+\n          '<div class=\"rhs\"><\/div>'+\n        '<\/div>';\n      }\n\n      html +=\n        \/\/ Row 3: Operator + route label\n        '<div class=\"tp-mrow\">'+\n          '<div class=\"lhs\">'+opCell(r)+'<\/div>'+\n          '<div class=\"rhs\"><span class=\"tp-route-ticket\">'+routeTicketHtml(routeLabel(r.routeId))+'<\/span><\/div>'+\n        '<\/div>'+\n      '<\/div>';\n    });\n    html += '<\/div>';\n    return html;\n  }\n\n  function renderExtendedCards(rows){\n    const ctaText = L.more_link || 'More sailings';\n    let html = '<div class=\"tp-cardlist\">';\n    rows.forEach(r=>{\n      const dow = WD[r.dep.getDay()] || '';\n      const link = r.opLink ? ('<a class=\"tp-ext-cta\" href=\"'+r.opLink+'\" target=\"_blank\" rel=\"nofollow noopener\">'+ctaText+' <span class=\"tp-ext-cta-arrow\">\u00bb<\/span><\/a>') : '<span class=\"tp-ext-cta\">'+ctaText+' <span class=\"tp-ext-cta-arrow\">\u00bb<\/span><\/span>';\n      html += '<div class=\"tp-ext-card\">'\n        + boostersHtml(r, true)\n        + '<div class=\"tp-ext-left\">'\n        +   '<div>'+opCell(r)+'<\/div>'\n        +   (showShip ? ('<div>'+shipBadge(r.ship)+'<\/div>') : '')\n        + '<\/div>'\n        + '<div class=\"tp-ext-mid\">'\n        +   '<div class=\"tp-ext-top\"><span class=\"tp-sub\">'+dShort(r.dep)+' <span class=\"tp-day\">'+dow+'<\/span><\/span><span class=\"tp-ext-dur\">'+dur(r.min)+'<\/span><\/div>'\n        +   '<div class=\"tp-ext-times\"><span class=\"tp-ext-time\">'+hhmm(r.dep)+'<\/span><span class=\"tp-ext-line\"><\/span><span class=\"tp-ext-dur\">'+dur(r.min)+'<\/span><span class=\"tp-ext-line\"><\/span><span class=\"tp-ext-time\">'+hhmm(r.arr)+'<\/span><\/div>'\n        +   '<div class=\"tp-ext-route\">'+routeLabel(r.routeId)+'<\/div>'\n        + '<\/div>'\n        + '<div class=\"tp-ext-right\">'+link+'<\/div>'\n        + '<\/div>';\n    });\n    html += '<\/div>';\n    return html;\n  }\n\n  \/\/ Safe fetch helper\n  async function fetchOne(routeId, from, to){\n    try{\n      const url = new URL(cfg.rest);\n      url.searchParams.set('route', String(routeId));\n      url.searchParams.set('from', from);\n      url.searchParams.set('to', to);\n      const res = await fetch(url.toString(), {credentials:'same-origin'});\n      if(!res.ok) return { rid: routeId, error: 'HTTP '+res.status };\n      const json = await res.json();\n      const list = (json && json.data && Array.isArray(json.data.rows)) ? json.data.rows : [];\n      return { rid: routeId, rows: list };\n    }catch(e){\n      return { rid: routeId, error: String(e && e.message ? e.message : e) };\n    }\n  }\n\n  async function load(){\n    btn.disabled = true; setLoader(true); status.textContent = L.status_loading;\n\n    const start = new Date(dateEl.value || new Date());\n    const days  = (daySetup === 'modern') ? 1 : Math.max(1, parseInt(daysEl.value,10)||1);\n    const end   = new Date(start); end.setDate(start.getDate()+days-1);\n    const fromIso = iso(start), toIso = iso(end);\n\n    try{\n      const results  = await Promise.all((cfg.routes||[]).map(rid=>fetchOne(rid, fromIso, toIso)));\n      const oks  = results.filter(r => !r.error);\n      const errs = results.filter(r =>  r.error);\n\n      const all = [];\n      oks.forEach(({rid, rows})=>{\n        rows.forEach(t=>{\n          all.push({\n            routeId: rid,\n            opId: t.operatorId || null,\n            opName: t.operatorName || '',\n            opLogo: t.operatorLogo || '',\n            opLink: t.operatorLink || '',\n            dep: toLocal(t.departureTime),\n            arr: toLocal(t.arrivalTime),\n            min: t.durationInMinutes || 0,\n            ship: t.shipName || ''\n          });\n        });\n      });\n\n      let rows = all;\n      if (Array.isArray(cfg.opFilter) && cfg.opFilter.length>0) {\n        rows = all.filter(r => r.opId && cfg.opFilter.includes(String(r.opId)));\n      }\n\n      rows.sort((a,b)=> a.dep - b.dep);\n\n      const todayStr = new Date().toDateString();\n      const nowMs = Date.now();\n      const arrivedToday = rows\n        .filter(r => (r.dep instanceof Date) && (r.arr instanceof Date) && r.dep.toDateString() === todayStr && r.arr.getTime() <= nowMs)\n        .sort((a,b) => b.arr - a.arr);\n      if (arrivedToday.length > 2) {\n        const keep = new Set(arrivedToday.slice(0,2).map(rowKey));\n        rows = rows.filter(r => {\n          const isArrivedToday = (r.dep instanceof Date) && (r.arr instanceof Date) && r.dep.toDateString() === todayStr && r.arr.getTime() <= nowMs;\n          return !isArrivedToday || keep.has(rowKey(r));\n        });\n      }\n\n      const total = rows.length;\n      if (total===0){\n        status.textContent = L.status_empty + (errs.length ? ' (some routes returned no data or errors)' : '');\n        clearStatusInline();\n        out.innerHTML='';\n        setLoader(false); btn.disabled=false; return;\n      }\n\n      const visibleRows = (limit10 && !revealAll && rows.length > 10) ? rows.slice(0,10) : rows;\n      const hiddenCount = rows.length - visibleRows.length;\n      soonKey = pickSoonKey(visibleRows);\n      featuredKey = pickFeaturedKey(visibleRows, soonKey);\n\n      status.textContent = errs.length ? ('Skipped '+errs.length+' route'+(errs.length>1?'s':'')) : '';\n      setStatusInline('<strong>'+total+'<\/strong> sailings found');\n      out.innerHTML = renderTable(visibleRows) + renderCards(visibleRows) + moreButtonHtml(hiddenCount);\n      bindRowLinks();\n      if (navHost) navHost.innerHTML = navHtml();\n\n    } catch(e){\n      status.textContent = 'Failed to load data';\n      out.innerHTML = '<pre>'+String(e.message||e)+'<\/pre>';\n    } finally {\n      setLoader(false);\n      btn.disabled = false;\n    }\n  }\n\n  wrap.addEventListener('click', function(e){\n    const nav = e.target.closest('.tp-day-nav a[data-shift]');\n    if (nav) {\n      e.preventDefault();\n      shiftDate(parseInt(nav.getAttribute('data-shift'),10) || 0);\n      return;\n    }\n    const more = e.target.closest('.tp-more-btn');\n    if (more) {\n      e.preventDefault();\n      revealAll = true;\n      load();\n    }\n  });\n\n  btn.addEventListener('click', load);\n  if (navHost) navHost.innerHTML = navHtml();\n  renderLocalTimes();\n  setInterval(renderLocalTimes, 30000);\n  if (cfg.autoload) load();\n})();\n<\/script>\n\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Yderligere information om k\u00f8replanen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Vil du vide mere om skibene? L\u00e6s mere om den p\u00e5g\u00e6ldende f\u00e6rge:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/da\/ms-bergensfjord\/\">MS Bergensfjord<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/da\/ms-stavangerfjord\/\">MS Stavangerfjord<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/da\/fjord-fstr\/\">Fjord FSTR<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/da\/color-line-superspeed-1\/\">Color Line SuperSpeed 1<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Vigtigt: Venligst tjek ind til tiden<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bem\u00e6rk venligst, at du b\u00f8r checke ind tidligt, da b\u00e5de havnene i Kristiansand og Hirtshals kan v\u00e6re meget travle til tider. Tjek ogs\u00e5 de specifikke terminaloplysninger for begge rederier:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Color Line:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/da\/hirtshals-havn\/color-line-terminal\/\">Color Line Terminal Hirtshals<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/da\/kristiansand-havn\/color-line-terminal\/\">Color Line Terminal Kristiansand<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Fjord Line:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/da\/hirtshals-havn\/fjord-line-terminal\/\">Fjord Line Terminal Hirtshals<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/da\/kristiansand-havn\/fjord-line-terminal\/\">Fjord Line Terminal Kristiansand<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Se den aktuelle k\u00f8replan for f\u00e6rgeruten Hirtshals Kristiansand (drevet af Fjord Line og Color Line). Hirtshals Kristiansand afgangs- og ankomsttider [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":121,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"inline_featured_image":false,"_uag_custom_page_level_css":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-56","page","type-page","status-publish","has-post-thumbnail","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Hirtshals Kristiansand sejlplan - F\u00e6rge Hirtshals Kristiansand<\/title>\n<meta name=\"description\" content=\"Se den aktuelle Hirtshals Kristiansand sejlplan her. Fartplanen for Color Line og Fjord Line.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/\" \/>\n<meta property=\"og:locale\" content=\"da_DK\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Hirtshals Kristiansand sejlplan - F\u00e6rge Hirtshals Kristiansand\" \/>\n<meta property=\"og:description\" content=\"Se den aktuelle Hirtshals Kristiansand sejlplan her. Fartplanen for Color Line og Fjord Line.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/\" \/>\n<meta property=\"og:site_name\" content=\"F\u00e6rge Hirtshals Kristiansand\" \/>\n<meta property=\"article:modified_time\" content=\"2026-05-19T12:51:17+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"800\" \/>\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=\"Estimeret l\u00e6setid\" \/>\n\t<meta name=\"twitter:data1\" content=\"2 minutter\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/sejlplan\\\/\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/sejlplan\\\/\",\"name\":\"Hirtshals Kristiansand sejlplan - F\u00e6rge Hirtshals Kristiansand\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/sejlplan\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/sejlplan\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/wp-content\\\/uploads\\\/sites\\\/4\\\/2024\\\/03\\\/hirtshals-kristiansand-ferry-2.jpg\",\"datePublished\":\"2024-03-11T11:14:26+00:00\",\"dateModified\":\"2026-05-19T12:51:17+00:00\",\"description\":\"Se den aktuelle Hirtshals Kristiansand sejlplan her. Fartplanen for Color Line og Fjord Line.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/sejlplan\\\/#breadcrumb\"},\"inLanguage\":\"da-DK\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/sejlplan\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"da-DK\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/sejlplan\\\/#primaryimage\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/wp-content\\\/uploads\\\/sites\\\/4\\\/2024\\\/03\\\/hirtshals-kristiansand-ferry-2.jpg\",\"contentUrl\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/wp-content\\\/uploads\\\/sites\\\/4\\\/2024\\\/03\\\/hirtshals-kristiansand-ferry-2.jpg\",\"width\":1200,\"height\":800,\"caption\":\"sejlplan\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/sejlplan\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Hirtshals Kristiansand\",\"item\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Hirtshals Kristiansand sejlplan\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/#website\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/\",\"name\":\"F\u00e6rge Hirtshals Kristiansand\",\"description\":\"Alt om f\u00e6rgeoverfarten fra Hirtshals til Kristiansand\",\"publisher\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"da-DK\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/#organization\",\"name\":\"F\u00e6rge Hirtshals Kristiansand\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"da-DK\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/wp-content\\\/uploads\\\/sites\\\/4\\\/2025\\\/05\\\/hirtshals-kristiansand.svg\",\"contentUrl\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/wp-content\\\/uploads\\\/sites\\\/4\\\/2025\\\/05\\\/hirtshals-kristiansand.svg\",\"width\":800,\"height\":256,\"caption\":\"F\u00e6rge Hirtshals Kristiansand\"},\"image\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/da\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Hirtshals Kristiansand sejlplan - F\u00e6rge Hirtshals Kristiansand","description":"Se den aktuelle Hirtshals Kristiansand sejlplan her. Fartplanen for Color Line og Fjord Line.","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:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/","og_locale":"da_DK","og_type":"article","og_title":"Hirtshals Kristiansand sejlplan - F\u00e6rge Hirtshals Kristiansand","og_description":"Se den aktuelle Hirtshals Kristiansand sejlplan her. Fartplanen for Color Line og Fjord Line.","og_url":"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/","og_site_name":"F\u00e6rge Hirtshals Kristiansand","article_modified_time":"2026-05-19T12:51:17+00:00","og_image":[{"width":1200,"height":800,"url":"https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Estimeret l\u00e6setid":"2 minutter"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/","url":"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/","name":"Hirtshals Kristiansand sejlplan - F\u00e6rge Hirtshals Kristiansand","isPartOf":{"@id":"https:\/\/hirtshals-kristiansand.com\/da\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/#primaryimage"},"image":{"@id":"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/#primaryimage"},"thumbnailUrl":"https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2.jpg","datePublished":"2024-03-11T11:14:26+00:00","dateModified":"2026-05-19T12:51:17+00:00","description":"Se den aktuelle Hirtshals Kristiansand sejlplan her. Fartplanen for Color Line og Fjord Line.","breadcrumb":{"@id":"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/#breadcrumb"},"inLanguage":"da-DK","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/"]}]},{"@type":"ImageObject","inLanguage":"da-DK","@id":"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/#primaryimage","url":"https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2.jpg","contentUrl":"https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2.jpg","width":1200,"height":800,"caption":"sejlplan"},{"@type":"BreadcrumbList","@id":"https:\/\/hirtshals-kristiansand.com\/da\/sejlplan\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Hirtshals Kristiansand","item":"https:\/\/hirtshals-kristiansand.com\/da\/"},{"@type":"ListItem","position":2,"name":"Hirtshals Kristiansand sejlplan"}]},{"@type":"WebSite","@id":"https:\/\/hirtshals-kristiansand.com\/da\/#website","url":"https:\/\/hirtshals-kristiansand.com\/da\/","name":"F\u00e6rge Hirtshals Kristiansand","description":"Alt om f\u00e6rgeoverfarten fra Hirtshals til Kristiansand","publisher":{"@id":"https:\/\/hirtshals-kristiansand.com\/da\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/hirtshals-kristiansand.com\/da\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"da-DK"},{"@type":"Organization","@id":"https:\/\/hirtshals-kristiansand.com\/da\/#organization","name":"F\u00e6rge Hirtshals Kristiansand","url":"https:\/\/hirtshals-kristiansand.com\/da\/","logo":{"@type":"ImageObject","inLanguage":"da-DK","@id":"https:\/\/hirtshals-kristiansand.com\/da\/#\/schema\/logo\/image\/","url":"https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2025\/05\/hirtshals-kristiansand.svg","contentUrl":"https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2025\/05\/hirtshals-kristiansand.svg","width":800,"height":256,"caption":"F\u00e6rge Hirtshals Kristiansand"},"image":{"@id":"https:\/\/hirtshals-kristiansand.com\/da\/#\/schema\/logo\/image\/"}}]}},"uagb_featured_image_src":{"full":["https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2.jpg",1200,800,false],"thumbnail":["https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2-150x150.jpg",150,150,true],"medium":["https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2-300x200.jpg",300,200,true],"medium_large":["https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2-768x512.jpg",768,512,true],"large":["https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2-1024x683.jpg",1024,683,true],"1536x1536":["https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2.jpg",1200,800,false],"2048x2048":["https:\/\/hirtshals-kristiansand.com\/da\/wp-content\/uploads\/sites\/4\/2024\/03\/hirtshals-kristiansand-ferry-2.jpg",1200,800,false]},"uagb_author_info":{"display_name":"mail@robberttigchelaar.com","author_link":"https:\/\/hirtshals-kristiansand.com\/da\/author\/mailrobberttigchelaar-com\/"},"uagb_comment_info":0,"uagb_excerpt":"Se den aktuelle k\u00f8replan for f\u00e6rgeruten Hirtshals Kristiansand (drevet af Fjord Line og Color Line). Hirtshals Kristiansand afgangs- og ankomsttider [&hellip;]","_links":{"self":[{"href":"https:\/\/hirtshals-kristiansand.com\/da\/wp-json\/wp\/v2\/pages\/56","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hirtshals-kristiansand.com\/da\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/hirtshals-kristiansand.com\/da\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/hirtshals-kristiansand.com\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hirtshals-kristiansand.com\/da\/wp-json\/wp\/v2\/comments?post=56"}],"version-history":[{"count":6,"href":"https:\/\/hirtshals-kristiansand.com\/da\/wp-json\/wp\/v2\/pages\/56\/revisions"}],"predecessor-version":[{"id":236,"href":"https:\/\/hirtshals-kristiansand.com\/da\/wp-json\/wp\/v2\/pages\/56\/revisions\/236"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hirtshals-kristiansand.com\/da\/wp-json\/wp\/v2\/media\/121"}],"wp:attachment":[{"href":"https:\/\/hirtshals-kristiansand.com\/da\/wp-json\/wp\/v2\/media?parent=56"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}