{"id":51,"date":"2024-03-11T13:20:14","date_gmt":"2024-03-11T13:20:14","guid":{"rendered":"https:\/\/hirtshals-kristiansand.com\/no\/?page_id=51"},"modified":"2026-10-02T10:37:38","modified_gmt":"2026-10-02T10:37:38","slug":"rutetider","status":"publish","type":"page","link":"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/","title":{"rendered":"Rutetider for Hirtshals-Kristiansand"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Se rutetider for fergen mellom Hirtshals og Kristiansand, b\u00e5de til Norge og Danmark. Color Line og Fjord Line betjener ruten, med avganger som varierer etter sesong og ukedag. Kontroller alltid aktuell avgang f\u00f8r du bestiller, spesielt hvis du reiser med bil, bobil eller campingvogn.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Kristiansand \u27a2 Hirtshals rutetider<\/h2>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Skal du reise fra Norge til Danmark? Her ser du avganger og ankomsttider fra Kristiansand havn til Hirtshals. Tabellen viser som standard de neste 6 dagene, men du kan selv endre visningen og se avganger opptil 14 dager frem i tid. Dette er den mest relevante retningen for mange norske reisende som skal videre i Danmark, til Tyskland eller lenger s\u00f8rover i Europa.<\/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\\\/no\\\/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;Dager&quot;,&quot;ui_button&quot;:&quot;Vis avganger&quot;,&quot;status_idle&quot;:&quot;Velg en dato og klikk p\\u00e5 Vis avganger&quot;,&quot;status_loading&quot;:&quot;Laster...&quot;,&quot;status_empty&quot;:&quot;Ingen avganger funnet&quot;,&quot;status_found&quot;:&quot;%d avganger funnet&quot;,&quot;th_date&quot;:&quot;Dato&quot;,&quot;th_dep&quot;:&quot;Avgang&quot;,&quot;th_arr&quot;:&quot;Ankomst&quot;,&quot;th_dur&quot;:&quot;Varighet&quot;,&quot;th_ship&quot;:&quot;Skip&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 avganger i rutetabellen v\\u00e5r&quot;,&quot;summary_footer_two&quot;:&quot;\\u00bb Se flere avganger for %1$s og %2$s i rutetabellen v\\u00e5r&quot;,&quot;summary_footer_generic&quot;:&quot;Sjekk rutetabellen v\\u00e5r for flere avganger&quot;,&quot;summary_header&quot;:&quot;De to neste avgangene:&quot;,&quot;summary_no_upcoming&quot;:&quot;Ingen kommende avganger funnet&quot;,&quot;more_link&quot;:&quot;Flere avganger&quot;,&quot;more_departures_button&quot;:&quot;Vis flere avganger&quot;,&quot;day_nav_previous&quot;:&quot;Vis forrige dag&quot;,&quot;day_nav_next&quot;:&quot;Vis neste dag&quot;,&quot;day_nav_today&quot;:&quot;Vis i dag&quot;,&quot;day_nav_tomorrow&quot;:&quot;Vis i morgen&quot;,&quot;local_time_pair&quot;:&quot;Lokal tid avgangshavn: %1$s \\\/ Lokal tid ankomsthavn: %2$s&quot;,&quot;local_time_combined&quot;:&quot;Lokal tid avgangs- og ankomsthavn: %s&quot;,&quot;tag_often_booked&quot;:&quot;\\u2b50 Ofte bestilt&quot;,&quot;tag_leaving_soon&quot;:&quot;\\ud83d\\udd34 G\\u00e5r snart&quot;,&quot;tag_currently_sailing&quot;:&quot;\\ud83d\\udfe2 P\\u00e5 vei n\\u00e5&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 avganger&quot;,&quot;shipMap&quot;:[],&quot;shipLinks&quot;:{&quot;bf&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/ms-bergensfjord\\\/&quot;,&quot;superspeed 1&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/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\">Dager<\/label><br>\n      <select id=\"tp-range-ops\" class=\"tp-range\" aria-label=\"Dager\">\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 avganger<\/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 avganger<\/h3>\n  \n  <div id=\"tp-status-ops\" class=\"tp-muted\" aria-live=\"polite\">Velg en dato og klikk p\u00e5 Vis avganger<\/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\">Laster...<\/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();\nlet prevLabel = L.day_nav_previous || 'Show previous day';\nlet nextLabel = L.day_nav_next || 'Show next day';\nif (isToday) nextLabel = L.day_nav_tomorrow || 'Show tomorrow';\nif (base.toDateString() === tomorrow.toDateString()) prevLabel = L.day_nav_today || '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\">'+(L.more_departures_button || '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(\n  (L.status_found || '%d sailings found').replace('%d', '<strong>'+total+'<\/strong>')\n);\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=\"wp-block-paragraph\">    <div class=\"fh-drive-tool\">\r\n        <h3>Beregn n\u00e5r du ankommer havnen i Hirtshals fra Norge<\/h3>\r\n        <p class=\"fh-text\">\r\n            Det kan v\u00e6re smart \u00e5 bruke verkt\u00f8yet nedenfor. Velg hvor du reiser fra, hvilken dag og tidspunkt du drar, og se omtrent n\u00e5r du sannsynligvis ankommer havnen i Hirtshals. P\u00e5 den m\u00e5ten kan du velge en ferge som passer godt med planene dine.\r\n        <\/p>\r\n\r\n        <div class=\"fh-field\">\r\n            <label for=\"fh_place\">Avreisested<\/label>\r\n            <select id=\"fh_place\">\r\n                <option value=\"oslo\" selected>Oslo<\/option>\r\n                <option value=\"kristiansand\">Kristiansand<\/option>\r\n                <option value=\"stavanger\">Stavanger<\/option>\r\n                <option value=\"bergen\">Bergen<\/option>\r\n                <option value=\"larvik\">Larvik<\/option>\r\n                <option value=\"sandefjord\">Sandefjord<\/option>\r\n                <option value=\"porsgrunn\">Porsgrunn<\/option>\r\n                <option value=\"drammen\">Drammen<\/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\">Avreisedag<\/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\">Avgangstid<\/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            oslo: 265,\r\n            kristiansand: 180,\r\n            stavanger: 370,\r\n            bergen: 510,\r\n            larvik: 155,\r\n            sandefjord: 170,\r\n            porsgrunn: 140,\r\n            drammen: 220\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 += 25;\r\n                if (minutes >= 900 && minutes < 1080) extra += 15;\r\n            }\r\n\r\n            if (['oslo','drammen','kristiansand'].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 = 'Velg f\u00f8rst et avgangstidspunkt.';\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 havnen i Hirtshals: ' + arrival + '<\/strong><br>' +\r\n                'Reisetiden er omtrent ' + minutesToText(total) + '. Denne beregningen tar hensyn til trafikk, rushtid, ukedager og mulige forsinkelser.<\/p>';\r\n        });\r\n    });\r\n    <\/script>\r\n    <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Hirtshals \u27a2 Kristiansand rutetider<\/h2>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Skal du reise tilbake til Norge? Her ser du avganger og ankomsttider fra Hirtshals havn til Kristiansand. Tabellen viser som standard de neste <strong>6 dagene<\/strong>, men du kan selv endre visningen og se avganger opptil 14 dager frem i tid. P\u00e5 travle reisedager kan enkelte avganger bli utsolgt, spesielt for bil, bobil og campingvogn.<\/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\\\/no\\\/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;Dager&quot;,&quot;ui_button&quot;:&quot;Vis avganger&quot;,&quot;status_idle&quot;:&quot;Velg en dato og klikk p\\u00e5 Vis avganger&quot;,&quot;status_loading&quot;:&quot;Laster...&quot;,&quot;status_empty&quot;:&quot;Ingen avganger funnet&quot;,&quot;status_found&quot;:&quot;%d avganger funnet&quot;,&quot;th_date&quot;:&quot;Dato&quot;,&quot;th_dep&quot;:&quot;Avgang&quot;,&quot;th_arr&quot;:&quot;Ankomst&quot;,&quot;th_dur&quot;:&quot;Varighet&quot;,&quot;th_ship&quot;:&quot;Skip&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 avganger i rutetabellen v\\u00e5r&quot;,&quot;summary_footer_two&quot;:&quot;\\u00bb Se flere avganger for %1$s og %2$s i rutetabellen v\\u00e5r&quot;,&quot;summary_footer_generic&quot;:&quot;Sjekk rutetabellen v\\u00e5r for flere avganger&quot;,&quot;summary_header&quot;:&quot;De to neste avgangene:&quot;,&quot;summary_no_upcoming&quot;:&quot;Ingen kommende avganger funnet&quot;,&quot;more_link&quot;:&quot;Flere avganger&quot;,&quot;more_departures_button&quot;:&quot;Vis flere avganger&quot;,&quot;day_nav_previous&quot;:&quot;Vis forrige dag&quot;,&quot;day_nav_next&quot;:&quot;Vis neste dag&quot;,&quot;day_nav_today&quot;:&quot;Vis i dag&quot;,&quot;day_nav_tomorrow&quot;:&quot;Vis i morgen&quot;,&quot;local_time_pair&quot;:&quot;Lokal tid avgangshavn: %1$s \\\/ Lokal tid ankomsthavn: %2$s&quot;,&quot;local_time_combined&quot;:&quot;Lokal tid avgangs- og ankomsthavn: %s&quot;,&quot;tag_often_booked&quot;:&quot;\\u2b50 Ofte bestilt&quot;,&quot;tag_leaving_soon&quot;:&quot;\\ud83d\\udd34 G\\u00e5r snart&quot;,&quot;tag_currently_sailing&quot;:&quot;\\ud83d\\udfe2 P\\u00e5 vei n\\u00e5&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 avganger&quot;,&quot;shipMap&quot;:[],&quot;shipLinks&quot;:{&quot;bf&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/ms-bergensfjord\\\/&quot;,&quot;superspeed 1&quot;:&quot;https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/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\">Dager<\/label><br>\n      <select id=\"tp-range-ops\" class=\"tp-range\" aria-label=\"Dager\">\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 avganger<\/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 avganger<\/h3>\n  \n  <div id=\"tp-status-ops\" class=\"tp-muted\" aria-live=\"polite\">Velg en dato og klikk p\u00e5 Vis avganger<\/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\">Laster...<\/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();\nlet prevLabel = L.day_nav_previous || 'Show previous day';\nlet nextLabel = L.day_nav_next || 'Show next day';\nif (isToday) nextLabel = L.day_nav_tomorrow || 'Show tomorrow';\nif (base.toDateString() === tomorrow.toDateString()) prevLabel = L.day_nav_today || '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\">'+(L.more_departures_button || '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(\n  (L.status_found || '%d sailings found').replace('%d', '<strong>'+total+'<\/strong>')\n);\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<h2 class=\"wp-block-heading\">Color Line rutetider og SuperSpeed 1<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Color Line seiler p\u00e5 ruten med <a href=\"https:\/\/hirtshals-kristiansand.com\/no\/color-line-superspeed-1\/\">SuperSpeed 1<\/a>. Dette er et av de mest brukte alternativene mellom Hirtshals og Kristiansand, s\u00e6rlig for reisende med bil, bobil eller campingvogn. Sjekk alltid aktuell rutetid, innsjekkingsfrist og terminal f\u00f8r avreise.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fjord Line rutetider<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Fjord Line har ogs\u00e5 avganger mellom Hirtshals og Kristiansand. Rutetidene kan variere gjennom \u00e5ret, og enkelte avganger kan v\u00e6re mer aktuelle i h\u00f8ysesong. Sammenlign derfor b\u00e5de avgangstid, ankomsttid, pris og reisetid f\u00f8r du velger billett.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Hvor lang tid tar fergen?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Reisetiden mellom Hirtshals og Kristiansand avhenger av selskap, fart\u00f8y og v\u00e6rforhold. Den raskeste overfarten, med Fjord FSTR i sesongen, tar rundt 2 timer og 25 minutter, mens andre avganger tar ca. 3 t 15 min (Color Line) eller 3 t 45\u20133 t 55 min (Fjord Lines cruiseskip). Se alltid p\u00e5 den konkrete avgangen i rutetabellen f\u00f8r du bestiller.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Skip p\u00e5 ruten<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Vil du vite mer om fart\u00f8yene som brukes p\u00e5 Hirtshals-Kristiansand? Les mer om skipene her:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/no\/ms-bergensfjord\/\">MS Bergensfjord<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/no\/ms-stavangerfjord\/\">MS Stavangerfjord<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/no\/fjord-fstr\/\">Fjord FSTR<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/no\/color-line-superspeed-1\/\">Color Line SuperSpeed 1<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Sjekk inn i god tid<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Kom til havnen i god tid f\u00f8r avreise. B\u00e5de Kristiansand og Hirtshals kan v\u00e6re travle i helger, ferier og p\u00e5 popul\u00e6re avganger. Reiser du med bil, bobil, campingvogn eller kj\u00e6ledyr, b\u00f8r du legge inn ekstra margin.<\/p>\n\n\n\n<div style=\"margin:20px 0;padding:16px 20px;background:#F0FBFF;border-left:4px solid #046bd2;border-radius:6px;\"><p style=\"margin:0;\"><strong style=\"color:#046bd2;\">V\u00e5rt tips:<\/strong> Tar du en tidlig avgang og har lang kj\u00f8retur til Kristiansand? Da er det enklest \u00e5 overnatte i byen kvelden f\u00f8r, s\u00e5 du slipper \u00e5 st\u00e5 opp midt p\u00e5 natten og er i god tid ved terminalen. Sammenlign hoteller i Kristiansand for natten f\u00f8r avreise. Skal du langt videre i Danmark, er et hotell i Hirtshals et godt stoppested etter ankomst.<\/p><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Sjekk ogs\u00e5 terminalinformasjonen for rederiet du reiser med:<\/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\/no\/kristiansand-havn\/color-line-terminal\/\">Color Line terminal i Kristiansand<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/no\/hirtshals-havn\/color-line-terminal\/\">Color Line terminal i Hirtshals<\/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\/no\/kristiansand-havn\/fjord-line-terminal\/\">Fjord Line terminal i Kristiansand<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/hirtshals-kristiansand.com\/no\/hirtshals-havn\/fjord-line-terminal\/\">Fjord Line terminal i Hirtshals<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Bestill fergebillett etter rutetid<\/h2>\n\n\n\n<div style=\"margin:20px 0;padding:16px 20px;background:#F0FBFF;border-left:4px solid #046bd2;border-radius:6px;\"><p style=\"margin:0;\"><strong style=\"color:#046bd2;\">V\u00e5rt tips:<\/strong> Har du funnet en avgang som passer? Sjekk pris og tilgjengelighet f\u00f8r du planlegger resten av reisen. Popul\u00e6re avganger kan bli dyrere eller utsolgt, s\u00e6rlig i juli, helger og ferier, og det gjelder spesielt plass til bil, bobil og campingvogn. <a href=\"https:\/\/hirtshals-kristiansand.com\/no\/go\/prices\" target=\"_blank\" rel=\"sponsored nofollow noopener\" style=\"color:#cc0000;font-weight:700;\">Sammenlign fergebilletter for din avgang \u2192<\/a><\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Se rutetider for fergen mellom Hirtshals og Kristiansand, b\u00e5de til Norge og Danmark. Color Line og Fjord Line betjener ruten, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":124,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_yoast_wpseo_focuskw":"rutetider hirtshals kristiansand","_yoast_wpseo_title":"Rutetider Kristiansand\u2013Hirtshals | Color Line & Fjord Line","_yoast_wpseo_metadesc":"Se dagens avganger Kristiansand-Hirtshals og tilbake. Rutetider for Color Line og Fjord Line de neste 6 dagene, med ankomsttid og reisetid.","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-51","page","type-page","status-publish","has-post-thumbnail","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Rutetider Kristiansand\u2013Hirtshals | Color Line &amp; Fjord Line<\/title>\n<meta name=\"description\" content=\"Se dagens avganger Kristiansand-Hirtshals og tilbake. Rutetider for Color Line og Fjord Line de neste 6 dagene, med ankomsttid og reisetid.\" \/>\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\/no\/rutetider\/\" \/>\n<meta property=\"og:locale\" content=\"nb_NO\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Rutetider Kristiansand\u2013Hirtshals | Color Line &amp; Fjord Line\" \/>\n<meta property=\"og:description\" content=\"Se dagens avganger Kristiansand-Hirtshals og tilbake. Rutetider for Color Line og Fjord Line de neste 6 dagene, med ankomsttid og reisetid.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/\" \/>\n<meta property=\"og:site_name\" content=\"Ferje Kristiansand Hirtshals\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-02T10:37:38+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line.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=\"Ansl. lesetid\" \/>\n\t<meta name=\"twitter:data1\" content=\"3 minutter\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/rutetider\\\/\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/rutetider\\\/\",\"name\":\"Rutetider Kristiansand\u2013Hirtshals | Color Line & Fjord Line\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/rutetider\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/rutetider\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2024\\\/03\\\/color-line.jpg\",\"datePublished\":\"2024-03-11T13:20:14+00:00\",\"dateModified\":\"2026-10-02T10:37:38+00:00\",\"description\":\"Se dagens avganger Kristiansand-Hirtshals og tilbake. Rutetider for Color Line og Fjord Line de neste 6 dagene, med ankomsttid og reisetid.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/rutetider\\\/#breadcrumb\"},\"inLanguage\":\"nb-NO\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/rutetider\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"nb-NO\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/rutetider\\\/#primaryimage\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2024\\\/03\\\/color-line.jpg\",\"contentUrl\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2024\\\/03\\\/color-line.jpg\",\"width\":1200,\"height\":800,\"caption\":\"color line\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/rutetider\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Kristiansand Hirtshals\",\"item\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Rutetider for Hirtshals-Kristiansand\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/#website\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/\",\"name\":\"Ferje Kristiansand Hirtshals\",\"description\":\"Alt om fergeoverfarten fra Hirtshals til Kristiansand\",\"publisher\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"nb-NO\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/#organization\",\"name\":\"Ferje Kristiansand Hirtshals\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"nb-NO\",\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2025\\\/05\\\/hirtshals-kristiansand.svg\",\"contentUrl\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2025\\\/05\\\/hirtshals-kristiansand.svg\",\"width\":800,\"height\":256,\"caption\":\"Ferje Kristiansand Hirtshals\"},\"image\":{\"@id\":\"https:\\\/\\\/hirtshals-kristiansand.com\\\/no\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Rutetider Kristiansand\u2013Hirtshals | Color Line & Fjord Line","description":"Se dagens avganger Kristiansand-Hirtshals og tilbake. Rutetider for Color Line og Fjord Line de neste 6 dagene, med ankomsttid og reisetid.","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\/no\/rutetider\/","og_locale":"nb_NO","og_type":"article","og_title":"Rutetider Kristiansand\u2013Hirtshals | Color Line & Fjord Line","og_description":"Se dagens avganger Kristiansand-Hirtshals og tilbake. Rutetider for Color Line og Fjord Line de neste 6 dagene, med ankomsttid og reisetid.","og_url":"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/","og_site_name":"Ferje Kristiansand Hirtshals","article_modified_time":"2026-10-02T10:37:38+00:00","og_image":[{"width":1200,"height":800,"url":"https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Ansl. lesetid":"3 minutter"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/","url":"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/","name":"Rutetider Kristiansand\u2013Hirtshals | Color Line & Fjord Line","isPartOf":{"@id":"https:\/\/hirtshals-kristiansand.com\/no\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/#primaryimage"},"image":{"@id":"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/#primaryimage"},"thumbnailUrl":"https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line.jpg","datePublished":"2024-03-11T13:20:14+00:00","dateModified":"2026-10-02T10:37:38+00:00","description":"Se dagens avganger Kristiansand-Hirtshals og tilbake. Rutetider for Color Line og Fjord Line de neste 6 dagene, med ankomsttid og reisetid.","breadcrumb":{"@id":"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/#breadcrumb"},"inLanguage":"nb-NO","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/"]}]},{"@type":"ImageObject","inLanguage":"nb-NO","@id":"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/#primaryimage","url":"https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line.jpg","contentUrl":"https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line.jpg","width":1200,"height":800,"caption":"color line"},{"@type":"BreadcrumbList","@id":"https:\/\/hirtshals-kristiansand.com\/no\/rutetider\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Kristiansand Hirtshals","item":"https:\/\/hirtshals-kristiansand.com\/no\/"},{"@type":"ListItem","position":2,"name":"Rutetider for Hirtshals-Kristiansand"}]},{"@type":"WebSite","@id":"https:\/\/hirtshals-kristiansand.com\/no\/#website","url":"https:\/\/hirtshals-kristiansand.com\/no\/","name":"Ferje Kristiansand Hirtshals","description":"Alt om fergeoverfarten fra Hirtshals til Kristiansand","publisher":{"@id":"https:\/\/hirtshals-kristiansand.com\/no\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/hirtshals-kristiansand.com\/no\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"nb-NO"},{"@type":"Organization","@id":"https:\/\/hirtshals-kristiansand.com\/no\/#organization","name":"Ferje Kristiansand Hirtshals","url":"https:\/\/hirtshals-kristiansand.com\/no\/","logo":{"@type":"ImageObject","inLanguage":"nb-NO","@id":"https:\/\/hirtshals-kristiansand.com\/no\/#\/schema\/logo\/image\/","url":"https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2025\/05\/hirtshals-kristiansand.svg","contentUrl":"https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2025\/05\/hirtshals-kristiansand.svg","width":800,"height":256,"caption":"Ferje Kristiansand Hirtshals"},"image":{"@id":"https:\/\/hirtshals-kristiansand.com\/no\/#\/schema\/logo\/image\/"}}]}},"uagb_featured_image_src":{"full":["https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line.jpg",1200,800,false],"thumbnail":["https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line-150x150.jpg",150,150,true],"medium":["https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line-300x200.jpg",300,200,true],"medium_large":["https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line-768x512.jpg",768,512,true],"large":["https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line-1024x683.jpg",1024,683,true],"1536x1536":["https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line.jpg",1200,800,false],"2048x2048":["https:\/\/hirtshals-kristiansand.com\/no\/wp-content\/uploads\/sites\/5\/2024\/03\/color-line.jpg",1200,800,false]},"uagb_author_info":{"display_name":"mail@robberttigchelaar.com","author_link":"https:\/\/hirtshals-kristiansand.com\/no\/author\/mailrobberttigchelaar-com\/"},"uagb_comment_info":0,"uagb_excerpt":"Se rutetider for fergen mellom Hirtshals og Kristiansand, b\u00e5de til Norge og Danmark. Color Line og Fjord Line betjener ruten, [&hellip;]","_links":{"self":[{"href":"https:\/\/hirtshals-kristiansand.com\/no\/wp-json\/wp\/v2\/pages\/51","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hirtshals-kristiansand.com\/no\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/hirtshals-kristiansand.com\/no\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/hirtshals-kristiansand.com\/no\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hirtshals-kristiansand.com\/no\/wp-json\/wp\/v2\/comments?post=51"}],"version-history":[{"count":18,"href":"https:\/\/hirtshals-kristiansand.com\/no\/wp-json\/wp\/v2\/pages\/51\/revisions"}],"predecessor-version":[{"id":346,"href":"https:\/\/hirtshals-kristiansand.com\/no\/wp-json\/wp\/v2\/pages\/51\/revisions\/346"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hirtshals-kristiansand.com\/no\/wp-json\/wp\/v2\/media\/124"}],"wp:attachment":[{"href":"https:\/\/hirtshals-kristiansand.com\/no\/wp-json\/wp\/v2\/media?parent=51"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}