{"id":87577,"date":"2026-10-08T05:00:02","date_gmt":"2026-10-08T12:00:02","guid":{"rendered":"https:\/\/www.redfin.com\/news\/?p=87577"},"modified":"2026-10-08T11:01:02","modified_gmt":"2026-10-08T18:01:02","slug":"return-to-normal-housing-costs","status":"publish","type":"post","link":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/","title":{"rendered":"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take."},"content":{"rendered":"\r\n<ul class=\"wp-block-list\">\r\n<li><strong><i>U.S. housing costs could return to \u201cnormal\u201d within the next five or six years if mortgage rates fall and\/or price growth flattens.\u00a0<\/i><\/strong><\/li>\r\n\r\n\r\n\r\n<li><strong><i>But the timeline varies widely depending on how rates and prices evolve\u2013and it varies even more from metro to metro.\u00a0<\/i><\/strong><\/li>\r\n\r\n\r\n\r\n<li><strong><i>Costs are closest to returning to normal in San Jose, Oakland, Seattle, Portland and Austin, where home prices are falling and incomes are projected to grow.\u00a0<\/i><\/strong><\/li>\r\n\r\n\r\n\r\n<li><strong><i>Housing costs could take at least a decade to return to normal in half of the metros in this analysis\u2013including the New York City area\u2013largely because home prices in those places are rising faster than incomes.<\/i><\/strong><\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">Housing costs could hypothetically return to \u201cnormal\u201d within the next five years if mortgage rates drop to 6% and home-price growth holds steady around 2.1%. Alternatively, housing costs could return to normal in just a slightly longer timeline\u2013within about six years\u2013if mortgage rates stay where they are today, about <\/span><a href=\"https:\/\/www.mortgagenewsdaily.com\/mortgage-rates\/30-year-fixed\"><span style=\"font-weight: 400;\">7.5%<\/span><\/a><span style=\"font-weight: 400;\">, and home-price growth flattens. For this report, housing costs are measured using the mortgage-payment-to-income ratio; see below for our definition of \u201cnormal.\u201d<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">If mortgage rates were to drop to the lowest bounds of our expectations\u20136%\u2013<\/span><i><span style=\"font-weight: 400;\">and <\/span><\/i><span style=\"font-weight: 400;\">price growth were to flatten, housing costs could return to normal by February 2029, which is just over two years from now. That\u2019s unlikely, but possible.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">On the flip side, it could take 10 years or more for costs to return to normal if mortgage rates remain stubbornly high, between 7% and 8%, and prices keep growing at their current annual rate of 2.1%. That\u2019s also unlikely but possible: If rates stay that high, home-price growth would be difficult to sustain without a further decline in home sales.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p><style>\r\n\r\n#housing-national-component {\r\n      --actual: #DE3341;\r\n      --growth-hi: #590213;\r\n      --growth-mid: #C76D86;\r\n      --growth-low: #F9BFD1;\r\n      --affordable: #BB9F45;\r\n      --projection-bg: #FEE8F4;\r\n      --accent-gold: #BB9F45;\r\n      --pale-gold: #FFEBAD;\r\n      --black: #111111;\r\n      --g900: #333333;\r\n      --g600: #6B6B6B;\r\n      --g300: #D9D9D9;\r\n      --g200: #EBEBEB;\r\n      --white: #FFFFFF;\r\n      --display: \"Libre Franklin\", Arial, sans-serif;\r\n      --body: \"Libre Franklin\", Arial, sans-serif;\r\n    }\r\n\r\n    #housing-national-component,\r\n#housing-national-component main,\r\n#housing-national-component section,\r\n#housing-national-component div,\r\n#housing-national-component span,\r\n#housing-national-component p,\r\n#housing-national-component h1,\r\n#housing-national-component h2,\r\n#housing-national-component h3,\r\n#housing-national-component button,\r\n#housing-national-component select,\r\n#housing-national-component label,\r\n#housing-national-component ul,\r\n#housing-national-component li,\r\n#housing-national-component small,\r\n#housing-national-component svg,\r\n#housing-national-component path,\r\n#housing-national-component circle,\r\n#housing-national-component text,\r\n#housing-national-component rect,\r\n#housing-national-component line,\r\n#housing-national-component polyline,\r\n#housing-national-component i { box-sizing: border-box; }\r\n    #housing-national-component,\r\n#housing-national-component { margin: 0; background: #fff; }\r\n    #housing-national-component {\r\n      color: var(--black);\r\n      font-family: var(--body);\r\n      font-size: 17px;\r\n      line-height: 1.55;\r\n      padding-left: env(safe-area-inset-left, 0);\r\n      padding-right: env(safe-area-inset-right, 0);\r\n      padding-bottom: env(safe-area-inset-bottom, 0);\r\n    }\r\n    #housing-national-component .wrap { max-width: 1040px; margin: 0 auto; padding: 0 20px; }\r\n    #housing-national-component section { padding: 36px 0 24px; }\r\n    #housing-national-component h1 {\r\n      margin: 0 0 8px;\r\n      font-family: var(--display);\r\n      font-size: 26px;\r\n      line-height: 1.15;\r\n      font-weight: 700;\r\n      letter-spacing: -.015em;\r\n    }\r\n    #housing-national-component .subhed {\r\n      max-width: 820px;\r\n      margin: 0 0 18px;\r\n      color: var(--g900);\r\n      font-size: 16px;\r\n      line-height: 1.42;\r\n    }\r\n\r\n    #housing-national-component .controls {\r\n      display: flex;\r\n      flex-wrap: wrap;\r\n      align-items: center;\r\n      gap: 8px 12px;\r\n      margin: 8px 0 12px;\r\n    }\r\n    #housing-national-component .control-label,\r\n#housing-national-component .axis-title {\r\n      font-family: var(--display);\r\n      font-size: 14px;\r\n      line-height: 1.2;\r\n    }\r\n    #housing-national-component .control-label { font-weight: 700; }\r\n    #housing-national-component .axis-title { font-weight: 400; color: var(--g600); }\r\n    #housing-national-component .axis-title { margin: 18px 0 2px; }\r\n\r\n    #housing-national-component .pills { display: flex; flex-wrap: wrap; gap: 6px; }\r\n    #housing-national-component .pill {\r\n      appearance: none;\r\n      -webkit-appearance: none;\r\n      font-family: var(--display);\r\n      font-size: 14px;\r\n      min-height: 32px;\r\n      min-width: 52px;\r\n      padding: 4px 12px;\r\n      border-radius: 999px;\r\n      border: 1px solid var(--g600);\r\n      background: #fff;\r\n      color: var(--black);\r\n      cursor: pointer;\r\n    }\r\n    #housing-national-component .pill:hover { border-color: var(--black); }\r\n    #housing-national-component .pill[aria-pressed=\"true\"] {\r\n      background: var(--growth-hi);\r\n      border-color: var(--growth-hi);\r\n      color: #fff;\r\n      font-weight: 700;\r\n    }\r\n    #housing-national-component .pill:focus-visible { outline: 2px solid var(--growth-hi); outline-offset: 2px; }\r\n\r\n    #housing-national-component .chart { width: 100%; margin-top: 0; }\r\n    #housing-national-component .chart svg { display: block; width: 100%; height: auto; overflow: visible; }\r\n    #housing-national-component svg text { font-family: var(--display); fill: var(--g600); font-size: 12px; }\r\n    #housing-national-component .grid { stroke: var(--g200); stroke-width: 1; }\r\n    #housing-national-component .axis-line { stroke: var(--g300); stroke-width: 1; }\r\n    #housing-national-component .projection-rule { stroke: var(--g300); stroke-width: 1; stroke-dasharray: 3 4; }\r\n    #housing-national-component .projection-label { fill: var(--g600); font-size: 11px; }\r\n    #housing-national-component .actual-line { fill: none; stroke: var(--actual); stroke-width: 2.8; }\r\n    #housing-national-component .affordable-line { fill: none; stroke: var(--affordable); stroke-width: 2.8; }\r\n    #housing-national-component .affordable-projected { stroke-dasharray: 3 5; }\r\n    #housing-national-component .scenario-line { fill: none; stroke-width: 2.4; }\r\n    #housing-national-component .scenario-low,\r\n#housing-national-component .scenario-mid,\r\n#housing-national-component .scenario-high { stroke-dasharray: 7 6; }\r\n    #housing-national-component .direct-label {\r\n      font-size: 12px;\r\n      font-weight: 700;\r\n      paint-order: stroke;\r\n      stroke: #fff;\r\n      stroke-width: 4px;\r\n      stroke-linejoin: round;\r\n    }\r\n    #housing-national-component .growth-label { font-size: 12px; font-weight: 700; }\r\n    #housing-national-component .cross-dot { stroke-width: 2.5; fill: #fff; }\r\n    #housing-national-component .cross-number { fill: var(--black); font-size: 8px; font-weight: 700; text-anchor: middle; dominant-baseline: central; pointer-events: none; }\r\n    #housing-national-component .annotation-title { fill: var(--g900); font-size: 12px; font-weight: 400; }\r\n    #housing-national-component .annotation-body { fill: var(--g900); font-size: 12px; font-weight: 400; }\r\n    #housing-national-component .annotation-line { fill: none; stroke: var(--black); stroke-width: 1.15; stroke-linecap: round; }\r\n    #housing-national-component .annotation-arrow { fill: var(--black); }\r\n    #housing-national-component .mobile-note ol { margin: 0; padding: 0; list-style: none; }\r\n    #housing-national-component .mobile-note li { position: relative; padding-left: 26px; margin: 0 0 8px; }\r\n    #housing-national-component .mobile-note li:last-child { margin-bottom: 0; }\r\n    #housing-national-component .mobile-note .mobile-index {\r\n      position: absolute; left: 0; top: 0.05em;\r\n      width: 18px; height: 18px; border-radius: 50%;\r\n      border: 1px solid var(--black); color: var(--black);\r\n      font-size: 11px; font-weight: 700; line-height: 16px; text-align: center;\r\n      background: #fff;\r\n    }\r\n\r\n    #housing-national-component .cap,\r\n#housing-national-component .mobile-note {\r\n      font-family: var(--display);\r\n      font-size: 12px;\r\n      line-height: 1.45;\r\n      color: var(--g600);\r\n    }\r\n    #housing-national-component .cap { margin: 8px 0 0; }\r\n    #housing-national-component .mobile-note {\r\n      display: none;\r\n      margin: 10px 0 2px;\r\n      padding: 10px 12px;\r\n      background: rgba(254,232,244,.21);\r\n      color: var(--g900);\r\n    }\r\n    #housing-national-component .mobile-note b { color: var(--black); }\r\n    #housing-national-component .error { padding: 22px 0; color: var(--g600); font: 14px\/1.5 var(--display); }\r\n\r\n    @media (min-width:700px) { #housing-national-component .wrap { padding: 0 48px; } }\r\n\r\n    @media (max-width:640px) {\r\n      #housing-national-component section { padding-top: 26px; }\r\n      #housing-national-component h1 { font-size: clamp(22px, 6.8vw, 26px); }\r\n      #housing-national-component .subhed { font-size: 15px; margin-bottom: 16px; }\r\n      #housing-national-component .controls { gap: 6px 10px; }\r\n      #housing-national-component .axis-title { margin-top: 14px; }\r\n      #housing-national-component .pill { font-size: 13px; min-height: 30px; min-width: 48px; padding: 3px 10px; }\r\n    }\r\n\r\n    @media (max-width:559px) {\r\n      #housing-national-component .controls { align-items: flex-start; flex-direction: column; gap: 6px; }\r\n      #housing-national-component .mobile-note { display: block; }\r\n      #housing-national-component .direct-label { font-size: 10.5px; }\r\n      #housing-national-component .growth-label { font-size: 10.5px; }\r\n      #housing-national-component .cap { font-size: 11px; }\r\n    }\r\n\r\n    @media (max-width:360px) {\r\n      #housing-national-component .wrap { padding: 0 14px; }\r\n      #housing-national-component section { padding-top: 22px; }\r\n      #housing-national-component h1 { font-size: 22px; }\r\n      #housing-national-component .subhed { font-size: 14px; }\r\n      #housing-national-component .pills { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%; gap: 5px; }\r\n      #housing-national-component .pill { width: 100%; min-width: 0; padding-left: 6px; padding-right: 6px; }\r\n      #housing-national-component .axis-title,\r\n#housing-national-component .control-label { font-size: 13px; }\r\n    }\r\n\r\n<\/style><\/p>\r\n<div id=\"housing-national-component\"><main class=\"wrap\">\r\n<section aria-labelledby=\"chart-title\">\r\n<h2 id=\"chart-title\">When Will U.S. Housing Costs Return to \u201cNormal\u201d?<\/h2>\r\n<div class=\"controls\"><span class=\"control-label\">Mortgage rate<\/span>\r\n<div id=\"rate-pills\" class=\"pills\" role=\"group\" aria-label=\"Mortgage rate\">\u00a0<\/div>\r\n<\/div>\r\n<div class=\"axis-title\">Median U.S. sale price<\/div>\r\n<div id=\"chart\" class=\"chart\" role=\"img\" aria-label=\"National actual and affordable home prices over time under different mortgage-rate scenarios.\">\u00a0<\/div>\r\n<div id=\"mobile-note\" class=\"mobile-note\">\u00a0<\/div>\r\n<\/section>\r\n<\/main><\/div>\r\n<p><script>\r\n  (() => {\r\n    const COMPONENT = document.getElementById('housing-national-component');\r\n    const DATA_URL = 'https:\/\/redfin-public-data.s3.amazonaws.com\/report_htmls\/frothiness_report\/national-metro-projection.csv';\r\n\r\n    const GROWTH = [\r\n      { key: 'neg2pct', label: '\u22122.0% growth', shortLabel: '\u22122.0%', color: '#F9BFD1', cls: 'scenario-low' },\r\n      { key: '0pct',    label: '0% growth',    shortLabel: '0%',    color: '#C76D86', cls: 'scenario-mid' },\r\n      { key: '2pct',    label: '+2.1% growth', shortLabel: '+2.1%', color: '#590213', cls: 'scenario-high' }\r\n    ];\r\n    const AFFORDABLE_COLOR = '#BB9F45';\r\n    const ACTUAL_COLOR = '#DE3341';\r\n    const PROJECTION_BG = '#FEE8F4';\r\n    const RATE_OPTIONS = [0.06, 0.065, 0.07, 0.0725, 0.075, 0.08];\r\n\r\n    let rows = [];\r\n    let selectedRate = null;\r\n    let resizeTimer;\r\n\r\n    function cleanHeader(h) {\r\n      return String(h || '').replace(\/\\u00a0\/g, ' ').trim().replace(\/\\s+\/g, '_').toLowerCase();\r\n    }\r\n\r\n    function parseCSV(text) {\r\n      const out = [];\r\n      let row = [], field = '', quoted = false;\r\n      for (let i = 0; i < text.length; i++) {\r\n        const ch = text[i], next = text[i + 1];\r\n        if (ch === '\"') {\r\n          if (quoted) {\r\n            if (next === '\"') { field += '\"'; i++; }\r\n            else quoted = false;\r\n          } else quoted = true;\r\n        } else if (ch === ',') {\r\n          if (quoted) field += ch;\r\n          else { row.push(field); field = ''; }\r\n        } else if (ch === '\\n' || ch === '\\r') {\r\n          if (quoted) field += ch;\r\n          else {\r\n            if (ch === '\\r') { if (next === '\\n') i++; }\r\n            row.push(field); field = '';\r\n            if (row.some(v => v.trim() !== '')) out.push(row);\r\n            row = [];\r\n          }\r\n        } else field += ch;\r\n      }\r\n      if (field.length || row.length) {\r\n        row.push(field);\r\n        if (row.some(v => v.trim() !== '')) out.push(row);\r\n      }\r\n      const headers = out.shift().map(cleanHeader);\r\n      return out.map(r => Object.fromEntries(headers.map((h, i) => [h, (r[i] ?? '').trim()])));\r\n    }\r\n\r\n    function number(v) {\r\n      const n = Number(String(v ?? '').replace(\/[$,%\\s]\/g, '').replace(\/,\/g, ''));\r\n      return Number.isFinite(n) ? n : null;\r\n    }\r\n\r\n    function parseDate(v) {\r\n      const d = new Date(v);\r\n      return Number.isFinite(d.getTime()) ? d : null;\r\n    }\r\n\r\n    function fmtMoney(v) {\r\n      return Number.isFinite(v) ? '$' + Math.round(v \/ 1000) + 'K' : '';\r\n    }\r\n\r\n    function fmtRate(v) {\r\n      const n = number(v);\r\n      if (n == null) return String(v);\r\n      const pct = Math.round(n * 10000) \/ 100;\r\n      return (Number.isInteger(pct * 10) ? pct.toFixed(1) : pct.toFixed(2)) + '%';\r\n    }\r\n\r\n\r\n    const CALLOUT_SWOOP_OFFSET = 0.35;\r\n\r\n    function swoopQuadPath(a, b, offset) {\r\n      const dx = b[0] - a[0];\r\n      const dy = b[1] - a[1];\r\n      const len = Math.hypot(dx, dy);\r\n      const perpAngle = Math.atan2(dy, dx) + Math.PI \/ 2;\r\n      const mx = (a[0] + b[0]) \/ 2;\r\n      const my = (a[1] + b[1]) \/ 2;\r\n      const cx = mx + Math.cos(perpAngle) * len * offset;\r\n      const cy = my + Math.sin(perpAngle) * len * offset;\r\n      return `M${a[0]},${a[1]} Q${cx},${cy} ${b[0]},${b[1]}`;\r\n    }\r\n\r\n    function diagonalSwoopSign(direction) {\r\n      const isTop = direction.startsWith('top');\r\n      const isLeftSide = direction.endsWith('left');\r\n      return isTop === isLeftSide ? -CALLOUT_SWOOP_OFFSET : CALLOUT_SWOOP_OFFSET;\r\n    }\r\n\r\n    function directionBetween(a, b) {\r\n      const vertical = a[1] <= b[1] ? 'bottom' : 'top';\r\n      const horizontal = a[0] <= b[0] ? 'right' : 'left';\r\n      return vertical + horizontal;\r\n    }\r\n\r\n    function unique(arr) { return [...new Set(arr)]; }\r\n    function scale(d0, d1, r0, r1) { return v => r0 + (v - d0) \/ (d1 - d0 || 1) * (r1 - r0); }\r\n    function pathFrom(points, x, y) {\r\n      return points.map((p, i) => `${i ? 'L' : 'M'}${x(p.date.getTime()).toFixed(2)},${y(p.value).toFixed(2)}`).join(' ');\r\n    }\r\n\r\n    function buildRateButtons() {\r\n      const rates = unique(rows.map(d => d.mortgage_rate).filter(Boolean))\r\n        .filter(r => RATE_OPTIONS.some(o => Math.abs(number(r) - o) < 1e-6))\r\n        .sort((a, b) => number(a) - number(b));\r\n\r\n      if (!rates.length) return;\r\n      selectedRate = rates.find(r => Math.abs(number(r) - 0.075) < 1e-6) || rates[0];\r\n\r\n      const box = COMPONENT.querySelector('#rate-pills');\r\n      box.innerHTML = rates.map(rate =>\r\n        `<button class=\"pill\" type=\"button\" data-rate=\"${rate}\" aria-pressed=\"${rate === selectedRate}\">${fmtRate(rate)}<\/button>`\r\n      ).join('');\r\n\r\n      box.addEventListener('click', e => {\r\n        const b = e.target.closest('button[data-rate]');\r\n        if (!b) return;\r\n        selectedRate = b.dataset.rate;\r\n        box.querySelectorAll('.pill').forEach(btn =>\r\n          btn.setAttribute('aria-pressed', btn === b ? 'true' : 'false')\r\n        );\r\n        render();\r\n      });\r\n    }\r\n\r\n    function crossingFor(scenarioRows, affordable) {\r\n      const aff = new Map(affordable.map(d => [d.date.getTime(), d.value]));\r\n      const pts = scenarioRows\r\n        .filter(d => d.period_type === 'projected')\r\n        .map(d => ({ t: d.date.getTime(), a: d.actual_price, b: aff.get(d.date.getTime()) }))\r\n        .filter(d => Number.isFinite(d.a) ? Number.isFinite(d.b) : false);\r\n\r\n      for (let i = 1; i < pts.length; i++) {\r\n        const d0 = pts[i - 1].a - pts[i - 1].b;\r\n        const d1 = pts[i].a - pts[i].b;\r\n        if (d0 === 0) return { t: pts[i - 1].t, v: pts[i - 1].a };\r\n        if (d1 === 0) return { t: pts[i].t, v: pts[i].a };\r\n        if (Math.sign(d0) !== Math.sign(d1)) {\r\n          const f = d0 \/ (d0 - d1);\r\n          return {\r\n            t: pts[i - 1].t + f * (pts[i].t - pts[i - 1].t),\r\n            v: pts[i - 1].a + f * (pts[i].a - pts[i - 1].a)\r\n          };\r\n        }\r\n      }\r\n      return null;\r\n    }\r\n\r\n    function yearOf(t) {\r\n      const d = new Date(t);\r\n      return d.getMonth() >= 6 ? d.getFullYear() + 1 : d.getFullYear();\r\n    }\r\n\r\n    function render() {\r\n      const chart = COMPONENT.querySelector('#chart');\r\n      if (!selectedRate) return;\r\n\r\n      const base = rows.filter(d =>\r\n        d.region_name === 'National' ?\r\n        (d.property_type === 'All Residential' ?\r\n          Math.abs(number(d.mortgage_rate) - number(selectedRate)) < 1e-6 : false) : false\r\n      );\r\n\r\n      if (!base.length) {\r\n        chart.innerHTML = '<div class=\"error\">No National \/ All Residential rows found for this mortgage rate.<\/div>';\r\n        return;\r\n      }\r\n\r\n      const scenarioSeries = GROWTH.map(g => ({\r\n        ...g,\r\n        rows: base.filter(d => d.price_growth_scenario === g.key).sort((a, b) => a.date - b.date)\r\n      })).filter(s => s.rows.length);\r\n      const baselineRows = scenarioSeries.find(s => s.key === '0pct')?.rows || scenarioSeries[0].rows;\r\n\r\n      const actualHist = baselineRows\r\n        .filter(d => d.period_type !== 'projected')\r\n        .map(d => ({ date: d.date, value: d.actual_price }))\r\n        .filter(d => d.date ? Number.isFinite(d.value) : false);\r\n\r\n      const affordable = baselineRows\r\n        .map(d => ({ date: d.date, value: d.affordable_price, period_type: d.period_type }))\r\n        .filter(d => d.date ? Number.isFinite(d.value) : false);\r\n\r\n      const projectedGrowth = scenarioSeries.flatMap(s =>\r\n        s.rows.filter(d => d.period_type === 'projected')\r\n          .map(d => ({ date: d.date, value: d.actual_price }))\r\n      );\r\n\r\n      const allPoints = [...actualHist, ...affordable, ...projectedGrowth];\r\n      const minDate = new Date(Math.min(...allPoints.map(d => d.date.getTime())));\r\n      const maxDate = new Date(Math.max(...allPoints.map(d => d.date.getTime())));\r\n      const minVal = Math.min(...allPoints.map(d => d.value));\r\n      const maxVal = Math.max(...allPoints.map(d => d.value));\r\n\r\n      const width = Math.max(292, chart.clientWidth || 900);\r\n      const phone = width < 560;\r\n      const annotationMobile = width < 520;\r\n      const tiny = width < 380;\r\n      const margin = {\r\n        top: 12,\r\n        right: phone ? (tiny ? 38 : 54) : 112,\r\n        bottom: 38,\r\n        left: phone ? 52 : 70\r\n      };\r\n      const h = tiny ? 342 : phone ? 380 : 520;\r\n      const iw = width - margin.left - margin.right;\r\n      const ih = h - margin.top - margin.bottom;\r\n      const x = scale(minDate.getTime(), maxDate.getTime(), margin.left, margin.left + iw);\r\n\r\n      const span = maxVal - minVal;\r\n      const yMin = Math.max(0, Math.floor((minVal - span * .08) \/ 50000) * 50000);\r\n      const yMax = Math.ceil((maxVal + span * .08) \/ 50000) * 50000;\r\n      const y = scale(yMin, yMax, margin.top + ih, margin.top);\r\n\r\n      const projectionDates = base.filter(d => d.period_type === 'projected').map(d => d.date).filter(Boolean);\r\n      const projectionStart = projectionDates.length ? new Date(Math.min(...projectionDates.map(d => d.getTime()))) : null;\r\n      const projectionX = projectionStart ? x(projectionStart.getTime()) : null;\r\n\r\n      let s = `<svg viewBox=\"0 0 ${width} ${h}\" aria-hidden=\"true\">\r\n        <defs>\r\n          <marker id=\"callout-arrow\" markerWidth=\"7\" markerHeight=\"7\" refX=\"6\" refY=\"3.5\" orient=\"auto\" markerUnits=\"strokeWidth\">\r\n            <path class=\"annotation-arrow\" d=\"M0,0 L7,3.5 L0,7 L1.6,3.5 Z\"\/>\r\n          <\/marker>\r\n        <\/defs>`;\r\n\r\n      if (projectionStart) {\r\n        s += `<rect x=\"${projectionX}\" y=\"${margin.top}\" width=\"${margin.left + iw - projectionX}\" height=\"${ih}\" fill=\"${PROJECTION_BG}\" fill-opacity=\"0.21\"\/>`;\r\n      }\r\n\r\n      const yTicks = 5;\r\n      for (let i = 0; i <= yTicks; i++) {\r\n        const v = yMin + (yMax - yMin) * i \/ yTicks;\r\n        const yy = y(v);\r\n        s += `<line class=\"grid\" x1=\"${margin.left}\" x2=\"${margin.left + iw}\" y1=\"${yy}\" y2=\"${yy}\"\/>`;\r\n        s += `<text x=\"0\" y=\"${yy + 4}\" text-anchor=\"start\">${fmtMoney(v)}<\/text>`;\r\n      }\r\n\r\n      const yearStep = tiny ? 6 : phone ? 4 : 3;\r\n      const startYear = minDate.getFullYear();\r\n      const endYear = maxDate.getFullYear();\r\n      for (let yr = Math.ceil(startYear \/ yearStep) * yearStep; yr <= endYear; yr += yearStep) {\r\n        const xx = x(new Date(yr, 7, 1).getTime());\r\n        s += `<text x=\"${xx}\" y=\"${margin.top + ih + 26}\" text-anchor=\"middle\">${yr}<\/text>`;\r\n      }\r\n      s += `<line class=\"axis-line\" x1=\"${margin.left}\" x2=\"${margin.left + iw}\" y1=\"${margin.top + ih}\" y2=\"${margin.top + ih}\"\/>`;\r\n\r\n      if (projectionStart) {\r\n        s += `<line class=\"projection-rule\" x1=\"${projectionX}\" x2=\"${projectionX}\" y1=\"${margin.top}\" y2=\"${margin.top + ih}\"\/>`;\r\n        s += `<text class=\"projection-label\" x=\"${projectionX + 6}\" y=\"${margin.top + 12}\">Projected<\/text>`;\r\n      }\r\n\r\n      \/\/ Historical actual price.\r\n      if (actualHist.length) {\r\n        s += `<path class=\"actual-line\" d=\"${pathFrom(actualHist, x, y)}\"\/>`;\r\n      }\r\n\r\n      \/\/ Affordable line; same path for all price-growth scenarios at a selected mortgage rate.\r\n      const affHist = affordable.filter(d => d.period_type !== 'projected');\r\n      const affProj = affordable.filter(d => d.period_type === 'projected');\r\n      if (affHist.length) s += `<path class=\"affordable-line\" d=\"${pathFrom(affHist, x, y)}\"\/>`;\r\n      if (affProj.length) {\r\n        const bridge = affHist.length ? [affHist[affHist.length - 1], ...affProj] : affProj;\r\n        s += `<path class=\"affordable-line affordable-projected\" d=\"${pathFrom(bridge, x, y)}\"\/>`;\r\n      }\r\n\r\n      \/\/ Direct labels for the two historical lines; no arrows.\r\n      if (actualHist.length) {\r\n        \/\/ On phones, place the label earlier on the historical line so it stays clear\r\n        \/\/ of the projection fan and numbered intersection markers. Keep it exactly\r\n        \/\/ 4px above the red line and rotate it to follow that local segment.\r\n        const idx = Math.max(1, Math.min(actualHist.length - 2, Math.floor(actualHist.length * (phone ? .58 : .72))));\r\n        const p = actualHist[idx];\r\n        const p0 = actualHist[idx - 1];\r\n        const p1 = actualHist[idx + 1];\r\n        const x0 = x(p0.date.getTime()), y0 = y(p0.value);\r\n        const x1 = x(p1.date.getTime()), y1 = y(p1.value);\r\n        const dx = x1 - x0, dy = y1 - y0;\r\n        const len = Math.hypot(dx, dy) || 1;\r\n        const actualLabelBaseAngle = Math.atan2(dy, dx) * 180 \/ Math.PI;\r\n        const actualLabelAngle = actualLabelBaseAngle + (window.innerWidth > 600 ? 10 : 0);\r\n        const lineX = x(p.date.getTime()) + (phone ? -4 : 5);\r\n        const lineY = y(p.value);\r\n        \/\/ Screen-space normal pointing upward from the line.\r\n        const nx = dy \/ len;\r\n        const ny = -dx \/ len;\r\n        const actualLabelX = lineX + nx * 5;\r\n        const actualLabelY = lineY + ny * 5;\r\n        s += `<text class=\"direct-label\" x=\"${actualLabelX}\" y=\"${actualLabelY}\" transform=\"rotate(${actualLabelAngle} ${actualLabelX} ${actualLabelY})\" style=\"fill:${ACTUAL_COLOR}\">Actual price<\/text>`;\r\n      }\r\n      if (affHist.length) {\r\n        const idx = Math.max(0, Math.min(affHist.length - 1, Math.floor(affHist.length * (phone ? .60 : .66))));\r\n        const p = affHist[idx];\r\n        const labelX = x(p.date.getTime()) + 7;\r\n        const labelY = y(p.value) + 18;\r\n        s += `<text class=\"direct-label\" x=\"${labelX}\" y=\"${labelY}\" style=\"fill:${AFFORDABLE_COLOR}\"><tspan x=\"${labelX}\">Affordable price<\/tspan><tspan x=\"${labelX}\" dy=\"14\">at ${fmtRate(selectedRate)}<\/tspan><\/text>`;\r\n      }\r\n\r\n      const crossings = [];\r\n      const endLabels = [];\r\n\r\n      scenarioSeries.forEach(sr => {\r\n        const proj = sr.rows\r\n          .filter(d => d.period_type === 'projected')\r\n          .map(d => ({ date: d.date, value: d.actual_price }))\r\n          .filter(d => d.date ? Number.isFinite(d.value) : false);\r\n        if (!proj.length) return;\r\n\r\n        const bridge = actualHist.length ? [actualHist[actualHist.length - 1], ...proj] : proj;\r\n        s += `<path class=\"scenario-line ${sr.cls}\" stroke=\"${sr.color}\" d=\"${pathFrom(bridge, x, y)}\"\/>`;\r\n\r\n        const last = proj[proj.length - 1];\r\n        endLabels.push({\r\n          lines: phone ? [sr.shortLabel, 'growth'] : [sr.label],\r\n          color: sr.color,\r\n          x: x(last.date.getTime()),\r\n          y: y(last.value)\r\n        });\r\n\r\n        const c = crossingFor(sr.rows, affordable);\r\n        if (c) crossings.push({ ...c, color: sr.color, key: sr.key, label: sr.label });\r\n      });\r\n\r\n      \/\/ Direct labels at the right ends of the scenario lines.\r\n      const minGap = phone ? 24 : 16;\r\n      endLabels.sort((a, b) => a.y - b.y);\r\n      for (let i = 1; i < endLabels.length; i++) {\r\n        if (endLabels[i].y - endLabels[i - 1].y < minGap) endLabels[i].y = endLabels[i - 1].y + minGap;\r\n      }\r\n      endLabels.forEach(l => {\r\n        const labelX = l.x + 7;\r\n        const labelY = phone ? l.y + 1 : l.y + 4;\r\n        if (l.lines.length === 1) {\r\n          s += `<text class=\"growth-label\" x=\"${labelX}\" y=\"${labelY}\" style=\"fill:${l.color}\">${l.lines[0]}<\/text>`;\r\n        } else {\r\n          s += `<text class=\"growth-label\" x=\"${labelX}\" y=\"${labelY}\" style=\"fill:${l.color}\"><tspan x=\"${labelX}\">${l.lines[0]}<\/tspan><tspan x=\"${labelX}\" dy=\"11\">${l.lines[1]}<\/tspan><\/text>`;\r\n        }\r\n      });\r\n\r\n      \/\/ Only draw circles for true line intersections.\r\n      \/\/ On mobile, number the scenarios explained below the chart:\r\n      \/\/ \u22122% = 1, flat = 2, +2% = 3 (when that crossing exists).\r\n      crossings.forEach(c => {\r\n        const mobileNumber = c.key === 'neg2pct' ? '1' : c.key === '0pct' ? '2' : c.key === '2pct' ? '3' : null;\r\n        if (phone) { if (!mobileNumber) return; }\r\n        const r = phone ? 7.2 : 6;\r\n        s += `<circle class=\"cross-dot\" cx=\"${x(c.t)}\" cy=\"${y(c.v)}\" r=\"${r}\" stroke=\"${c.color}\"\/>`;\r\n        if (phone) { if (mobileNumber) {\r\n          s += `<text class=\"cross-number\" x=\"${x(c.t)}\" y=\"${y(c.v) + 0.3}\">${mobileNumber}<\/text>`;\r\n          }\r\n        }\r\n      });\r\n\r\n      \/\/ Option B annotation: begin at the bottom-center of the 0% intersection circle.\r\n      const midCross = crossings.find(c => c.key === '0pct') || crossings[Math.floor(crossings.length \/ 2)];\r\n      if (!annotationMobile) { if (midCross) {\r\n        const cx = x(midCross.t), cy = y(midCross.v);\r\n        const circleR = phone ? 4.7 : 6;\r\n\r\n        \/\/ Keep the note fully inside the plot. As the chart narrows, the note wraps into\r\n        \/\/ shorter lines rather than spilling beyond the x-axis end.\r\n        const plotRight = margin.left + iw;\r\n        const plotBottom = margin.top + ih;\r\n        const compactAnnotation = width < 800;\r\n        \/\/ Reserve enough room for the longest SVG text line. SVG text does not\r\n        \/\/ auto-wrap, so compact mode uses deliberately short tspans and a hard\r\n        \/\/ right boundary inside the x-axis range.\r\n        const noteWidth = compactAnnotation ? 138 : 262;\r\n        const minNoteX = Math.max(projectionX + 10, margin.left + 8);\r\n        const maxNoteX = Math.max(minNoteX, plotRight - noteWidth - 10);\r\n        const desiredX = compactAnnotation ? cx - 46 : cx - 86;\r\n        const noteX = Math.max(minNoteX, Math.min(maxNoteX, desiredX));\r\n        const noteY = Math.min(plotBottom - (compactAnnotation ? 72 : 54), cy + (compactAnnotation ? 88 : 112));\r\n\r\n        \/\/ Start at the bottom edge of the circle, never through\/above it. The quadratic\r\n        \/\/ control point is also clamped below the circle so the swoop always travels downward.\r\n        const leaderStart = [cx, cy + circleR];\r\n        const leaderEnd = compactAnnotation\r\n          ? [Math.min(plotRight - 12, noteX + Math.min(noteWidth * 0.42, 58)), noteY - 8]\r\n          : [Math.min(plotRight - 12, noteX + Math.min(noteWidth * 0.58, 118)), noteY - 10];\r\n        const dx = leaderEnd[0] - leaderStart[0];\r\n        const dy = leaderEnd[1] - leaderStart[1];\r\n        const len = Math.hypot(dx, dy);\r\n        const perpAngle = Math.atan2(dy, dx) + Math.PI \/ 2;\r\n        const mx = (leaderStart[0] + leaderEnd[0]) \/ 2;\r\n        const my = (leaderStart[1] + leaderEnd[1]) \/ 2;\r\n        const rawCx = mx + Math.cos(perpAngle) * len * CALLOUT_SWOOP_OFFSET;\r\n        const rawCy = my + Math.sin(perpAngle) * len * CALLOUT_SWOOP_OFFSET;\r\n        const controlX = Math.min(plotRight - 8, Math.max(margin.left + 8, rawCx));\r\n        const controlY = Math.max(cy + circleR + 16, rawCy);\r\n        const leaderPath = `M${leaderStart[0]},${leaderStart[1]} Q${controlX},${controlY} ${leaderEnd[0]},${leaderEnd[1]}`;\r\n        s += `<path class=\"annotation-line\" marker-end=\"url(#callout-arrow)\" d=\"${leaderPath}\"\/>`;\r\n\r\n        const flatCrossDesktop = crossings.find(c => c.key === '0pct');\r\n        const flatYearDesktop = flatCrossDesktop ? yearOf(flatCrossDesktop.t) : null;\r\n        if (compactAnnotation) {\r\n          s += flatYearDesktop\r\n            ? `<text x=\"${noteX}\" y=\"${noteY}\" class=\"annotation-title\"><tspan x=\"${noteX}\">At ${fmtRate(selectedRate)}, costs<\/tspan><tspan x=\"${noteX}\" dy=\"15\">could return to \u201cnormal\u201d<\/tspan><tspan x=\"${noteX}\" dy=\"15\">around ${flatYearDesktop} if home<\/tspan><tspan x=\"${noteX}\" dy=\"15\">prices were to flatten.<\/tspan><\/text>`\r\n            : `<text x=\"${noteX}\" y=\"${noteY}\" class=\"annotation-title\"><tspan x=\"${noteX}\">At ${fmtRate(selectedRate)},<\/tspan><tspan x=\"${noteX}\" dy=\"15\">flat home prices do not<\/tspan><tspan x=\"${noteX}\" dy=\"15\">bring costs back to<\/tspan><tspan x=\"${noteX}\" dy=\"15\">\u201cnormal\u201d in this view.<\/tspan><\/text>`;\r\n        } else {\r\n          s += flatYearDesktop\r\n            ? `<text x=\"${noteX}\" y=\"${noteY}\" class=\"annotation-title\"><tspan x=\"${noteX}\">At ${fmtRate(selectedRate)}, costs could return to \u201cnormal\u201d<\/tspan><tspan x=\"${noteX}\" dy=\"16\">around ${flatYearDesktop} if home prices were to flatten.<\/tspan><\/text>`\r\n            : `<text x=\"${noteX}\" y=\"${noteY}\" class=\"annotation-title\"><tspan x=\"${noteX}\">At ${fmtRate(selectedRate)}, flat home prices do not bring<\/tspan><tspan x=\"${noteX}\" dy=\"16\">housing costs back to \u201cnormal\u201d in this view.<\/tspan><\/text>`;\r\n        }\r\n        }\r\n      }\r\n\r\n      s += '<\/svg>';\r\n      chart.innerHTML = s;\r\n\r\n      const mobileNote = COMPONENT.querySelector('#mobile-note');\r\n      const byKey = Object.fromEntries(crossings.map(c => [c.key, c]));\r\n      const lowYear = byKey.neg2pct ? yearOf(byKey.neg2pct.t) : null;\r\n      const flatYear = byKey['0pct'] ? yearOf(byKey['0pct'].t) : null;\r\n      const highYear = byKey['2pct'] ? yearOf(byKey['2pct'].t) : null;\r\n      if (lowYear || flatYear || highYear) {\r\n        const items = [];\r\n        if (lowYear) items.push(`<li><span class=\"mobile-index\">1<\/span>If prices fall 2.0% a year, costs return to \u201cnormal\u201d around ${lowYear}.<\/li>`);\r\n        if (flatYear) items.push(`<li><span class=\"mobile-index\">2<\/span>If prices stay flat, costs return to \u201cnormal\u201d around ${flatYear}.<\/li>`);\r\n        if (highYear) items.push(`<li><span class=\"mobile-index\">3<\/span>If prices rise 2.1% a year, costs return to \u201cnormal\u201d around ${highYear}.<\/li>`);\r\n        mobileNote.innerHTML = `<ol>${items.join('')}<\/ol>`;\r\n      } else {\r\n        mobileNote.innerHTML = `No projected scenario crosses the affordable-price line within the period shown at a ${fmtRate(selectedRate)} mortgage rate.`;\r\n      }\r\n    }\r\n\r\n    async function boot() {\r\n      const chart = COMPONENT.querySelector('#chart');\r\n      try {\r\n        const res = await fetch(DATA_URL, { cache: 'no-store' });\r\n        if (!res.ok) throw new Error(`Data request failed: ${res.status} ${res.statusText}`);\r\n        const raw = parseCSV(await res.text());\r\n        rows = raw.map(r => ({\r\n          region_name: r.region_name,\r\n          property_type: r.property_type,\r\n          period_type: r.period_type,\r\n          rate_scenario: r.rate_scenario,\r\n          mortgage_rate: r.mortgage_rate,\r\n          price_growth_scenario: r.price_growth_scenario,\r\n          actual_price: number(r.actual_price),\r\n          affordable_price: number(r.affordable_price),\r\n          date: parseDate(r.period_begin)\r\n        })).filter(d =>\r\n          d.region_name === 'National' ?\r\n          (d.property_type === 'All Residential' ? Boolean(d.date) : false) : false\r\n        );\r\n\r\n        if (!rows.length) throw new Error('No National \/ All Residential data found.');\r\n        buildRateButtons();\r\n        render();\r\n        window.addEventListener('resize', () => {\r\n          clearTimeout(resizeTimer);\r\n          resizeTimer = setTimeout(render, 120);\r\n        });\r\n      } catch (err) {\r\n        console.error(err);\r\n        chart.innerHTML = `<div class=\"error\"><b>Unable to load projection data.<\/b><br>${err.message}<\/div>`;\r\n      }\r\n    }\r\n\r\n    boot();\r\n    })();\r\n<\/script><\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\"><\/figure>\r\n\r\n\r\n\r\n<h2 id=\"h-how-we-define-normal-and-how-it-varies-based-on-where-you-live\" class=\"wp-block-heading\"><b>How We Define \u201cNormal\u201d \u2014 and How It Varies Based On Where You Live<\/b><\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">&nbsp;<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">This analysis explores hypothetical scenarios for U.S. home-price growth, mortgage rates and income levels, and uses those scenarios to estimate when housing costs could return to \u201cnormal.\u201d For this report, \u201cnormal\u201d means housing costs, defined as the mortgage-payment-to-income ratio, have returned to August 2018 levels. At that time, <\/span><span style=\"font-weight: 400;\">the national median monthly mortgage payment-to-income ratio was 30%\u2014meaning the typical U.S. homebuyer would need to spend 30% of their household income on their monthly mortgage payment. This 30% threshold is a widely recognized <\/span><a href=\"https:\/\/www.jchs.harvard.edu\/sites\/default\/files\/Harvard_JCHS_Herbert_Hermann_McCue_measuring_housing_affordability.pdf\"><span style=\"font-weight: 400;\">benchmark<\/span><\/a><span style=\"font-weight: 400;\"> for housing affordability.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">But at the metro level, <\/span><span style=\"font-weight: 400;\">\u201cnormal\u201d does not necessarily mean \u201caffordable;\u201d rather, \u201cnormal\u201d means the metro has returned to its 2018 level of home prices relative to incomes, even if the median home in some expensive metros remains out of reach for the typical household. Please see the end of this report for more details on methodology.<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">The analysis is theoretical, and the hypothetical scenarios should not be read as predictions. But they do represent real trends in home-price growth, mortgage rates and income growth, and any of them are possible.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">\u201cMany house hunters feel stuck between two bad options: Stretch themselves to buy at today\u2019s rates, or wait for lower rates only to see prices climb further out of reach,\u201d said Redfin Senior Economist <\/span><a href=\"https:\/\/www.redfin.com\/news\/author\/asad-khan\/\"><span style=\"font-weight: 400;\">Asad Khan<\/span><\/a><span style=\"font-weight: 400;\">. \u201cBut prospective buyers shouldn\u2019t get hung up on timing the market. These hypothetical scenarios should give would-be buyers and sellers some hope that the market can normalize with only modest changes in rates or prices. For buyers and sellers, that means the best time to make a move is when it makes sense for your finances and your life. If you\u2019re a buyer who needs more time to save for a down payment, take more time. If you\u2019re a buyer who has the means to buy at current costs and you find your dream home, don\u2019t let today\u2019s rates stop you.\u201d<\/span><\/p>\r\n\r\n\r\n\r\n<h2 id=\"h-housing-costs-are-almost-back-to-normal-in-parts-of-the-west-coast-nbsp\" class=\"wp-block-heading\"><b>Housing Costs Are Almost Back to \u201cNormal\u201d in Parts of the West Coast\u00a0<\/b><\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">&nbsp;<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">The timeline for return to normalcy varies by region.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">Housing costs are closest to returning to normal in San Jose, CA. San Jose\u2019s declining home prices (-3.2% year over year), combined with the fact that we expect strong future wage growth (6.5%), mean housing costs could return to normal in just over one year\u2014even with today\u2019s 7.5% mortgage rates. If rates were to fall to 6.5%, San Jose\u2019s housing costs could return to normal by the end of this year. That\u2019s largely due to stronger-than-average income growth, thanks to the Bay Area\u2019s <\/span><a href=\"https:\/\/www.redfin.com\/news\/two-tech-cities-housing-san-francisco-seattle\/\"><span style=\"font-weight: 400;\">tech-fueled<\/span><\/a><span style=\"font-weight: 400;\"> economy.<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">Note that San Jose is one of the places where \u201cnormal\u201d doesn\u2019t equal \u201caffordable.\u201d A median-earning Bay Area household would need to spend <\/span><a href=\"https:\/\/www.redfin.com\/news\/affordability-homebuying-2026\/\"><span style=\"font-weight: 400;\">much more than 30% of their income<\/span><\/a><span style=\"font-weight: 400;\"> to buy a home now, just as they would have in 2018.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p><style>\r\n\r\n#housing-map-component {\r\n      --red: #DE3341;\r\n      --red-dark: #590213;\r\n      --pink: #FEE8F4;\r\n      --putty: #F3EEE7;\r\n      --black: #111111;\r\n      --g900: #333333;\r\n      --g600: #6B6B6B;\r\n      --g500: #8A8A8A;\r\n      --g300: #D9D9D9;\r\n      --g200: #EBEBEB;\r\n      --navy: #2F3E5A;\r\n      --white: #FFFFFF;\r\n      --display: \"Libre Franklin\", sans-serif;\r\n      --body: \"Libre Franklin\", sans-serif;\r\n    }\r\n\r\n    #housing-map-component,\r\n#housing-map-component main,\r\n#housing-map-component section,\r\n#housing-map-component div,\r\n#housing-map-component span,\r\n#housing-map-component p,\r\n#housing-map-component h1,\r\n#housing-map-component h2,\r\n#housing-map-component h3,\r\n#housing-map-component button,\r\n#housing-map-component select,\r\n#housing-map-component label,\r\n#housing-map-component ul,\r\n#housing-map-component li,\r\n#housing-map-component small,\r\n#housing-map-component svg,\r\n#housing-map-component path,\r\n#housing-map-component circle,\r\n#housing-map-component text,\r\n#housing-map-component rect,\r\n#housing-map-component line,\r\n#housing-map-component polyline,\r\n#housing-map-component i {\r\n      box-sizing: border-box\r\n    }\r\n\r\n    #housing-map-component {\r\n      scroll-padding-top: 84px;\r\n      background: #fff\r\n    }\r\n\r\n    #housing-map-component {\r\n      margin: 0;\r\n      background: #fff;\r\n      color: var(--black);\r\n      font-family: var(--body);\r\n      font-size: 17px;\r\n      line-height: 1.6;\r\n      padding-left: env(safe-area-inset-left, 0);\r\n      padding-right: env(safe-area-inset-right, 0);\r\n      padding-bottom: env(safe-area-inset-bottom, 0)\r\n    }\r\n\r\n    #housing-map-component .wrap {\r\n      max-width: 1040px;\r\n      margin: 0 auto;\r\n      padding: 0 20px\r\n    }\r\n\r\n    @media (min-width:700px) {\r\n      #housing-map-component .wrap {\r\n        padding: 0 48px\r\n      }\r\n    }\r\n\r\n    #housing-map-component .prose {\r\n      max-width: 700px\r\n    }\r\n\r\n    #housing-map-component h1,\r\n#housing-map-component h2,\r\n#housing-map-component h3 {\r\n      font-family: var(--display);\r\n      color: var(--black);\r\n      margin: 0\r\n    }\r\n\r\n    #housing-map-component h1 {\r\n      font-size: 32px;\r\n      line-height: 1.15;\r\n      font-weight: 600;\r\n      letter-spacing: -.01em\r\n    }\r\n\r\n    #housing-map-component h2 {\r\n      font-size: 22px;\r\n      line-height: 1.25;\r\n      font-weight: 600;\r\n      padding-bottom: 0;\r\n      border-bottom: 0;\r\n      margin-bottom: 16px\r\n    }\r\n\r\n    #housing-map-component h2 .num {\r\n      color: var(--red);\r\n      margin-right: 10px\r\n    }\r\n\r\n    #housing-map-component h3 {\r\n      font-size: 17px;\r\n      font-weight: 600;\r\n      margin: 0 0 6px\r\n    }\r\n\r\n    #housing-map-component p {\r\n      margin: 0 0 16px\r\n    }\r\n\r\n    #housing-map-component .lede {\r\n      font-size: 19px;\r\n      color: var(--g900)\r\n    }\r\n\r\n    #housing-map-component header {\r\n      padding-top: calc(44px + env(safe-area-inset-top, 0));\r\n      padding-bottom: 20px\r\n    }\r\n\r\n    #housing-map-component .eyebrow {\r\n      font-family: var(--display);\r\n      font-size: 13px;\r\n      color: var(--g600);\r\n      letter-spacing: .02em;\r\n      margin-bottom: 8px\r\n    }\r\n\r\n    #housing-map-component section {\r\n      padding: 40px 0 8px\r\n    }\r\n\r\n    #housing-map-component .ui,\r\n#housing-map-component .cap,\r\n#housing-map-component button,\r\n#housing-map-component select,\r\n#housing-map-component label,\r\n#housing-map-component svg text {\r\n      font-family: var(--display)\r\n    }\r\n\r\n    #housing-map-component .cap {\r\n      font-size: 13px;\r\n      color: var(--g600);\r\n      letter-spacing: .02em;\r\n      margin: 6px 0 0\r\n    }\r\n\r\n    #housing-map-component .callout {\r\n      background: var(--putty);\r\n      border-left: 4px solid var(--red);\r\n      border-radius: 8px;\r\n      padding: 16px;\r\n      margin: 20px 0\r\n    }\r\n\r\n    #housing-map-component .callout p:last-child {\r\n      margin: 0\r\n    }\r\n\r\n    #housing-map-component .callout .lead {\r\n      font-weight: 700\r\n    }\r\n\r\n    #housing-map-component .takeaways {\r\n      list-style: none;\r\n      margin: 0;\r\n      padding: 0;\r\n      counter-reset: t\r\n    }\r\n\r\n    #housing-map-component .takeaways li {\r\n      counter-increment: t;\r\n      position: relative;\r\n      padding: 0 0 12px 36px\r\n    }\r\n\r\n    #housing-map-component .takeaways li:before {\r\n      content: counter(t);\r\n      position: absolute;\r\n      left: 0;\r\n      top: 0;\r\n      font-family: var(--display);\r\n      font-weight: 600;\r\n      font-size: 22px;\r\n      color: var(--red);\r\n      line-height: 1.3\r\n    }\r\n\r\n    #housing-map-component .takeaways b {\r\n      font-weight: 700\r\n    }\r\n\r\n    \/* rate bar *\/\r\n    #housing-map-component .ratebar {\r\n      position: sticky;\r\n      top: 0;\r\n      z-index: 20;\r\n      background: #fff;\r\n      border-bottom: 1px solid var(--g300);\r\n      padding: 10px 0;\r\n      padding-top: calc(10px + env(safe-area-inset-top, 0))\r\n    }\r\n\r\n    #housing-map-component .ratebar .wrap {\r\n      display: flex;\r\n      flex-wrap: wrap;\r\n      align-items: center;\r\n      gap: 8px 14px\r\n    }\r\n\r\n    #housing-map-component .ratebar .lbl {\r\n      font-family: var(--display);\r\n      font-size: 14px;\r\n      font-weight: 600\r\n    }\r\n\r\n    #housing-map-component .pills {\r\n      display: flex;\r\n      flex-wrap: wrap;\r\n      gap: 6px\r\n    }\r\n\r\n    #housing-map-component .pill {\r\n      font-family: var(--display);\r\n      font-size: 14px;\r\n      min-height: 32px;\r\n      min-width: 52px;\r\n      padding: 4px 12px;\r\n      border-radius: 999px;\r\n      border: 1px solid var(--g600);\r\n      background: #fff;\r\n      color: var(--black);\r\n      cursor: pointer\r\n    }\r\n\r\n    #housing-map-component .pill[aria-pressed=\"true\"] {\r\n      background: var(--red-dark);\r\n      color: #fff;\r\n      border-color: var(--red-dark)\r\n    }\r\n\r\n    #housing-map-component .pill:hover {\r\n      border-color: var(--black)\r\n    }\r\n\r\n    #housing-map-component :focus-visible {\r\n      outline: 2px solid var(--navy);\r\n      outline-offset: 2px\r\n    }\r\n\r\n    #housing-map-component .ratebar .hint {\r\n      font-size: 12px;\r\n      color: var(--g600);\r\n      font-family: var(--display)\r\n    }\r\n\r\n    \/* charts *\/\r\n    #housing-map-component .chart {\r\n      width: 100%;\r\n      margin: 8px 0 0\r\n    }\r\n\r\n    #housing-map-component .chart svg {\r\n      display: block;\r\n      width: 100%;\r\n      height: auto;\r\n      overflow: visible\r\n    }\r\n\r\n    #housing-map-component svg text {\r\n      font-size: 12px;\r\n      fill: var(--g900)\r\n    }\r\n\r\n    #housing-map-component svg .axis text {\r\n      fill: var(--g600)\r\n    }\r\n\r\n    #housing-map-component svg .anno {\r\n      fill: var(--black);\r\n      font-size: 13px\r\n    }\r\n\r\n    #housing-map-component svg .anno-l {\r\n      fill: var(--g600);\r\n      font-size: 12px\r\n    }\r\n\r\n    #housing-map-component svg .lab {\r\n      font-size: 12px;\r\n      fill: var(--black);\r\n      font-weight: 600\r\n    }\r\n\r\n    #housing-map-component .legend {\r\n      display: flex;\r\n      flex-wrap: wrap;\r\n      gap: 6px 20px;\r\n      font-family: var(--display);\r\n      font-size: 14px;\r\n      color: var(--g900);\r\n      margin: 8px 0 4px\r\n    }\r\n\r\n    #housing-map-component .legend span {\r\n      display: flex;\r\n      align-items: center;\r\n    }\r\n\r\n    #housing-map-component .legend i {\r\n      display: inline-block;\r\n      width: 16px;\r\n      height: 16px;\r\n      border-radius: 50%;\r\n      margin-right: 3px;\r\n      vertical-align: -1px\r\n    }\r\n\r\n    \/* section 1 bars *\/\r\n    #housing-map-component .pair {\r\n      margin: 0 0 22px\r\n    }\r\n\r\n    #housing-map-component .pair .who {\r\n      font-family: var(--display);\r\n      font-weight: 600;\r\n      font-size: 16px;\r\n      margin-bottom: 6px\r\n    }\r\n\r\n    #housing-map-component .row {\r\n      display: grid;\r\n      grid-template-columns: 118px 1fr;\r\n      gap: 10px;\r\n      align-items: center;\r\n      margin: 5px 0;\r\n      font-family: var(--display);\r\n      font-size: 13px\r\n    }\r\n\r\n    @media (min-width:600px) {\r\n      #housing-map-component .row {\r\n        grid-template-columns: 190px 1fr\r\n      }\r\n    }\r\n\r\n    #housing-map-component .track {\r\n      position: relative;\r\n      height: 26px;\r\n      background: transparent\r\n    }\r\n\r\n    #housing-map-component .bar {\r\n      height: 100%;\r\n      border-radius: 4px;\r\n      display: flex;\r\n      align-items: center;\r\n      justify-content: flex-end;\r\n      padding-right: 8px;\r\n      color: #fff;\r\n      font-weight: 600;\r\n      font-size: 13px;\r\n      min-width: 56px\r\n    }\r\n\r\n    #housing-map-component .bar.med {\r\n      background: var(--g600)\r\n    }\r\n\r\n    #housing-map-component .bar.aff {\r\n      background: var(--navy)\r\n    }\r\n\r\n    #housing-map-component .pair .verdict {\r\n      font-size: 15px;\r\n      margin-top: 6px\r\n    }\r\n\r\n    \/* lanes *\/\r\n    #housing-map-component .lane {\r\n      display: grid;\r\n      grid-template-columns: 1fr;\r\n      gap: 6px;\r\n      padding: 12px 0;\r\n      border-top: 1px solid var(--g200)\r\n    }\r\n\r\n    @media (min-width:640px) {\r\n      #housing-map-component .lane {\r\n        grid-template-columns: 130px 1fr\r\n      }\r\n    }\r\n\r\n    #housing-map-component .lane .ln {\r\n      font-family: var(--display);\r\n      font-size: 14px;\r\n      font-weight: 600\r\n    }\r\n\r\n    #housing-map-component .lane .ln small {\r\n      display: block;\r\n      font-weight: 400;\r\n      color: var(--g600);\r\n      font-size: 12px\r\n    }\r\n\r\n    #housing-map-component .chips {\r\n      display: flex;\r\n      flex-wrap: wrap;\r\n      gap: 6px\r\n    }\r\n\r\n    #housing-map-component .chip {\r\n      font-family: var(--display);\r\n      font-size: 13px;\r\n      min-height: 28px;\r\n      padding: 2px 10px;\r\n      border-radius: 999px;\r\n      border: 1px solid var(--g600);\r\n      background: #fff;\r\n      color: var(--black);\r\n      cursor: pointer\r\n    }\r\n\r\n    #housing-map-component .chip:hover {\r\n      border-color: var(--black);\r\n      background: var(--g200)\r\n    }\r\n\r\n    #housing-map-component .chip[aria-pressed=\"true\"] {\r\n      background: var(--red-dark);\r\n      color: #fff;\r\n      border-color: var(--red-dark);\r\n      font-weight: 600\r\n    }\r\n\r\n    #housing-map-component .chip.muted {\r\n      color: var(--g600);\r\n      border-color: var(--g300)\r\n    }\r\n\r\n    #housing-map-component .chip.static {\r\n      cursor: default;\r\n      border-color: var(--g300);\r\n      color: var(--g600)\r\n    }\r\n\r\n    #housing-map-component .chip.static:hover {\r\n      background: #fff;\r\n      border-color: var(--g300)\r\n    }\r\n\r\n    \/* metro card *\/\r\n    #housing-map-component .card {\r\n      border: 1px solid var(--g300);\r\n      border-radius: 8px;\r\n      padding: 20px;\r\n      margin: 8px 0 18px\r\n    }\r\n\r\n    #housing-map-component .card label {\r\n      font-size: 14px;\r\n      font-weight: 600;\r\n      display: block;\r\n      margin-bottom: 6px\r\n    }\r\n\r\n    #housing-map-component select {\r\n      font-size: 16px;\r\n      min-height: 44px;\r\n      padding: 6px 10px;\r\n      border: 1px solid var(--g600);\r\n      border-radius: 8px;\r\n      background: #fff;\r\n      color: var(--black);\r\n      max-width: 100%;\r\n      width: 100%\r\n    }\r\n\r\n    @media (min-width:600px) {\r\n      #housing-map-component select {\r\n        width: 320px\r\n      }\r\n    }\r\n\r\n    #housing-map-component .metro-native-select {\r\n      position: absolute;\r\n      width: 1px !important;\r\n      height: 1px;\r\n      padding: 0;\r\n      margin: -1px;\r\n      overflow: hidden;\r\n      clip: rect(0, 0, 0, 0);\r\n      white-space: nowrap;\r\n      border: 0;\r\n    }\r\n\r\n    #housing-map-component .metro-select-wrap {\r\n      position: relative;\r\n      display: inline-flex;\r\n      max-width: 100%;\r\n    }\r\n\r\n    #housing-map-component .metro-select-button {\r\n      appearance: none;\r\n      -webkit-appearance: none;\r\n      font-family: var(--display);\r\n      font-size: 15px;\r\n      font-weight: 600;\r\n      padding: 9px 38px 9px 16px;\r\n      min-height: 44px;\r\n      min-width: 260px;\r\n      max-width: 100%;\r\n      border-radius: 999px;\r\n      border: 1px solid var(--g600);\r\n      background: #fff;\r\n      color: var(--black);\r\n      cursor: pointer;\r\n      display: inline-flex;\r\n      align-items: center;\r\n      position: relative;\r\n      text-align: left;\r\n    }\r\n\r\n    #housing-map-component .metro-select-button:hover { border-color: var(--black); }\r\n\r\n    #housing-map-component .metro-select-button:focus-visible {\r\n      outline: 2px solid var(--navy);\r\n      outline-offset: 2px;\r\n    }\r\n\r\n    #housing-map-component .metro-select-caret {\r\n      position: absolute;\r\n      right: 15px;\r\n      top: 50%;\r\n      width: 7px;\r\n      height: 7px;\r\n      border-right: 2px solid currentColor;\r\n      border-bottom: 2px solid currentColor;\r\n      transform: translateY(-65%) rotate(45deg);\r\n      transition: transform 120ms ease;\r\n    }\r\n\r\n    #housing-map-component .metro-select-button[aria-expanded=\"true\"] .metro-select-caret {\r\n      transform: translateY(-35%) rotate(225deg);\r\n    }\r\n\r\n    #housing-map-component .metro-select-list {\r\n      position: absolute;\r\n      top: calc(100% + 6px);\r\n      left: 0;\r\n      z-index: 30;\r\n      margin: 0;\r\n      padding: 6px;\r\n      min-width: 100%;\r\n      max-height: 300px;\r\n      overflow-y: auto;\r\n      list-style: none;\r\n      background: #fff;\r\n      border: 1px solid var(--g200);\r\n      border-radius: 14px;\r\n      box-shadow: 0 10px 28px rgba(0,0,0,.14), 0 2px 6px rgba(0,0,0,.06);\r\n    }\r\n\r\n    #housing-map-component .metro-select-list[hidden] { display: none; }\r\n\r\n    #housing-map-component .metro-select-option {\r\n      display: flex;\r\n      align-items: center;\r\n      justify-content: space-between;\r\n      gap: 16px;\r\n      padding: 10px 12px;\r\n      border-radius: 10px;\r\n      font-family: var(--display);\r\n      font-size: 15px;\r\n      color: var(--black);\r\n      white-space: nowrap;\r\n      cursor: pointer;\r\n    }\r\n\r\n    #housing-map-component .metro-select-option + .metro-select-option {\r\n      margin-top: 2px;\r\n      border-top: 1px solid var(--g200);\r\n      padding-top: 12px;\r\n    }\r\n\r\n    #housing-map-component .metro-select-option:hover,\r\n#housing-map-component .metro-select-option.is-selected {\r\n      background: var(--g200);\r\n    }\r\n\r\n    #housing-map-component .metro-select-option.is-selected { font-weight: 600; }\r\n    #housing-map-component .metro-select-check { color: var(--black); font-size: 13px; }\r\n\r\n    @media (max-width:520px) {\r\n      #housing-map-component .metro-select-wrap,\r\n#housing-map-component .metro-select-button { width: 100%; }\r\n      #housing-map-component .metro-select-button { min-width: 0; }\r\n    }\r\n\r\n    #housing-map-component .big {\r\n      font-family: var(--display);\r\n      font-size: 22px;\r\n      font-weight: 600;\r\n      line-height: 1.15;\r\n      margin: 16px 0 2px\r\n    }\r\n\r\n    #housing-map-component .big small {\r\n      display: block;\r\n      font-size: 14px;\r\n      font-weight: 400;\r\n      color: var(--g600);\r\n      margin-top: 2px\r\n    }\r\n\r\n    #housing-map-component .tl {\r\n      position: relative;\r\n      height: 70px;\r\n      margin: 12px 6px 6px\r\n    }\r\n\r\n    #housing-map-component .tl .line {\r\n      position: absolute;\r\n      left: 0;\r\n      right: 0;\r\n      top: 26px;\r\n      height: 4px;\r\n      background: var(--g300);\r\n      border-radius: 2px\r\n    }\r\n\r\n    #housing-map-component .tl .fill {\r\n      position: absolute;\r\n      left: 0;\r\n      top: 26px;\r\n      height: 4px;\r\n      background: var(--red);\r\n      border-radius: 2px\r\n    }\r\n\r\n    #housing-map-component .tl .tick {\r\n      position: absolute;\r\n      top: 20px;\r\n      width: 1px;\r\n      height: 16px;\r\n      background: var(--g600)\r\n    }\r\n\r\n    #housing-map-component .tl .tl-l {\r\n      position: absolute;\r\n      top: 42px;\r\n      font-family: var(--display);\r\n      font-size: 12px;\r\n      color: var(--g600);\r\n      transform: translateX(-50%);\r\n      white-space: nowrap\r\n    }\r\n\r\n    #housing-map-component .tl .tl-l:first-of-type {\r\n      transform: none\r\n    }\r\n\r\n    #housing-map-component .tl .tl-l.last {\r\n      transform: translateX(-100%)\r\n    }\r\n\r\n    #housing-map-component .tl .mk {\r\n      position: absolute;\r\n      top: 17px;\r\n      width: 22px;\r\n      height: 22px;\r\n      margin-left: -11px;\r\n      border-radius: 50%;\r\n      background: var(--red);\r\n      border: 3px solid #fff;\r\n      box-shadow: 0 0 0 2px var(--red)\r\n    }\r\n\r\n    #housing-map-component .tl .mk.open {\r\n      background: #fff\r\n    }\r\n\r\n    #housing-map-component .tl .mkl {\r\n      position: absolute;\r\n      top: -4px;\r\n      font-family: var(--display);\r\n      font-size: 12px;\r\n      font-weight: 600;\r\n      transform: translateX(-50%);\r\n      white-space: nowrap\r\n    }\r\n\r\n    #housing-map-component .facts {\r\n      display: grid;\r\n      grid-template-columns: 1fr 1fr;\r\n      gap: 10px 20px;\r\n      margin: 16px 0;\r\n      font-family: var(--display)\r\n    }\r\n\r\n    #housing-map-component .facts div {\r\n      font-size: 13px;\r\n      color: var(--g600)\r\n    }\r\n\r\n    #housing-map-component .facts b {\r\n      display: block;\r\n      font-size: 22px;\r\n      color: var(--black);\r\n      font-weight: 600\r\n    }\r\n\r\n    #housing-map-component .sens {\r\n      display: flex;\r\n      flex-wrap: wrap;\r\n      gap: 6px;\r\n      margin: 8px 0 12px\r\n    }\r\n\r\n    #housing-map-component .sens span {\r\n      font-family: var(--display);\r\n      font-size: 13px;\r\n      border: 1px solid var(--g300);\r\n      border-radius: 8px;\r\n      padding: 4px 10px;\r\n      color: var(--g900);\r\n      background: #fff\r\n    }\r\n\r\n    #housing-map-component .sens span.on {\r\n      border-color: var(--red-dark);\r\n      background: var(--pink);\r\n      font-weight: 600;\r\n      color: var(--black)\r\n    }\r\n\r\n    #housing-map-component .sens small {\r\n      display: block;\r\n      font-size: 11px;\r\n      color: var(--g600);\r\n      font-weight: 400\r\n    }\r\n\r\n    #housing-map-component .grp {\r\n      margin: 14px 0\r\n    }\r\n\r\n    #housing-map-component .grp .gl {\r\n      font-family: var(--display);\r\n      font-size: 14px;\r\n      font-weight: 600;\r\n      margin-bottom: 4px\r\n    }\r\n\r\n    #housing-map-component .mini {\r\n      display: grid;\r\n      grid-template-columns: 110px 1fr;\r\n      gap: 8px;\r\n      align-items: center;\r\n      font-family: var(--display);\r\n      font-size: 12px;\r\n      color: var(--g900);\r\n      margin: 3px 0\r\n    }\r\n\r\n    #housing-map-component .mini .b {\r\n      height: 14px;\r\n      border-radius: 3px\r\n    }\r\n\r\n    #housing-map-component .mini .bw {\r\n      display: flex;\r\n      align-items: center;\r\n      gap: 8px\r\n    }\r\n\r\n    #housing-map-component .unlock {\r\n      font-family: var(--display);\r\n      font-size: 14px;\r\n      margin: 12px 0;\r\n      padding: 0;\r\n      list-style: none\r\n    }\r\n\r\n    #housing-map-component .unlock li {\r\n      padding: 6px 10px;\r\n      border-radius: 8px;\r\n      border: 1px solid transparent\r\n    }\r\n\r\n    #housing-map-component .unlock li.on {\r\n      background: var(--pink);\r\n      border-color: var(--red-dark)\r\n    }\r\n\r\n    #housing-map-component .notes h3 {\r\n      margin-top: 18px\r\n    }\r\n\r\n    #housing-map-component .notes ul {\r\n      padding-left: 20px;\r\n      margin: 0 0 16px\r\n    }\r\n\r\n    #housing-map-component .notes li {\r\n      margin-bottom: 8px\r\n    }\r\n\r\n    #housing-map-component footer {\r\n      padding: 32px 0 56px;\r\n      color: var(--g600);\r\n      font-family: var(--display);\r\n      font-size: 13px;\r\n      border-top: 1px solid var(--g300);\r\n      margin-top: 32px\r\n    }\r\n\r\n    #housing-map-component .sr {\r\n      position: absolute;\r\n      left: -9999px\r\n    }\r\n\r\n    @media (max-width:520px) {\r\n      #housing-map-component h1 {\r\n        font-size: 28px\r\n      }\r\n\r\n      #housing-map-component .big {\r\n        font-size: 22px\r\n      }\r\n    }\r\n\r\n    #housing-map-component .split {\r\n      display: flex;\r\n      height: 44px;\r\n      border-radius: 8px;\r\n      overflow: hidden;\r\n      margin: 14px 0 6px;\r\n      font-family: var(--display);\r\n      font-size: 13px;\r\n      font-weight: 600\r\n    }\r\n\r\n    #housing-map-component .split div {\r\n      display: flex;\r\n      align-items: center;\r\n      justify-content: center;\r\n      min-width: 0;\r\n      padding: 0 4px;\r\n      white-space: nowrap;\r\n      overflow: hidden\r\n    }\r\n\r\n    #housing-map-component .splitkey {\r\n      display: flex;\r\n      flex-wrap: wrap;\r\n      gap: 6px 16px;\r\n      font-family: var(--display);\r\n      font-size: 13px;\r\n      color: var(--g900);\r\n      margin: 0 0 16px\r\n    }\r\n\r\n    #housing-map-component .splitkey i {\r\n      display: inline-block;\r\n      width: 11px;\r\n      height: 11px;\r\n      border-radius: 3px;\r\n      margin-right: 6px;\r\n      vertical-align: -1px\r\n    }\r\n\r\n    #housing-map-component .bandh {\r\n      margin: 18px 0 6px;\r\n      padding-top: 10px;\r\n      border-top: 1px solid var(--g300)\r\n    }\r\n\r\n    #housing-map-component .bandh b {\r\n      font-family: var(--display);\r\n      font-size: 15px\r\n    }\r\n\r\n    #housing-map-component .bandh span {\r\n      display: block;\r\n      font-family: var(--display);\r\n      font-size: 13px;\r\n      color: var(--g600)\r\n    }\r\n\r\n    #housing-map-component .badge {\r\n      display: inline-block;\r\n      font-family: var(--display);\r\n      font-size: 12px;\r\n      font-weight: 600;\r\n      border-radius: 999px;\r\n      padding: 2px 10px;\r\n      margin-bottom: 6px\r\n    }\r\n\r\n    #housing-map-component .facts3 {\r\n      display: grid;\r\n      grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\r\n      gap: 10px 14px;\r\n      margin: 16px 0;\r\n      font-family: var(--display)\r\n    }\r\n\r\n    #housing-map-component .facts3 div {\r\n      font-size: 12px;\r\n      color: var(--g600)\r\n    }\r\n\r\n    #housing-map-component .facts3 b {\r\n      display: block;\r\n      font-size: 20px;\r\n      color: var(--black);\r\n      font-weight: 600\r\n    }\r\n\r\n    #housing-map-component .scrolly {\r\n      position: relative;\r\n      margin-top: 20px\r\n    }\r\n\r\n    #housing-map-component .sticky-chart {\r\n      position: sticky;\r\n      top: 0;\r\n      z-index: 5;\r\n      background: #fff;\r\n      padding: calc(env(safe-area-inset-top, 0px) + 6px) 0 6px;\r\n      border-bottom: 1px solid var(--g200)\r\n    }\r\n\r\n    #housing-map-component .sticky-chart .legend {\r\n      font-size: 12px;\r\n      margin: 2px 0\r\n    }\r\n\r\n    #housing-map-component .step {\r\n      min-height: 78vh;\r\n      padding: 16px 0 24px\r\n    }\r\n\r\n    #housing-map-component .step-card {\r\n      background: #fff;\r\n      border: 1px solid var(--g300);\r\n      border-left: 4px solid var(--red);\r\n      border-radius: 8px;\r\n      padding: 16px;\r\n      box-shadow: 0 1px 3px rgba(0, 0, 0, .08);\r\n      transition: opacity .3s\r\n    }\r\n\r\n    #housing-map-component .step-card .k {\r\n      font-family: var(--display);\r\n      font-size: 12px;\r\n      color: var(--g600);\r\n      letter-spacing: .02em;\r\n      margin-bottom: 4px\r\n    }\r\n\r\n    #housing-map-component .step-card h3 {\r\n      font-size: 19px;\r\n      margin-bottom: 8px\r\n    }\r\n\r\n    #housing-map-component .step-card p {\r\n      font-size: 16px;\r\n      margin: 0 0 10px\r\n    }\r\n\r\n    #housing-map-component .step-card p:last-child {\r\n      margin: 0\r\n    }\r\n\r\n    #housing-map-component .step:last-child {\r\n      min-height: 60vh\r\n    }\r\n\r\n    @media (max-width:899px) {\r\n      #housing-map-component .step {\r\n        padding-top: 20px\r\n      }\r\n    }\r\n\r\n    @media (prefers-reduced-motion:reduce) {\r\n      #housing-map-component .step-card {\r\n        transition: none\r\n      }\r\n    }\r\n\r\n    #housing-map-component .mc-el {\r\n      transition: opacity .5s ease\r\n    }\r\n\r\n    @media (prefers-reduced-motion:reduce) {\r\n      #housing-map-component .mc-el {\r\n        transition: none\r\n      }\r\n    }\r\n\r\n    #housing-map-component .step-card {\r\n      max-width: 560px;\r\n      margin: 0 auto\r\n    }\r\n\r\n    #housing-map-component .step {\r\n      min-height: 62vh;\r\n      padding: 20px 0 24px\r\n    }\r\n\r\n    #housing-map-component .step:last-child {\r\n      min-height: 55vh\r\n    }\r\n\r\n    #housing-map-component .opt-tag {\r\n      display: inline-block;\r\n      font-family: var(--display);\r\n      font-size: 12px;\r\n      font-weight: 600;\r\n      border: 1px solid var(--g600);\r\n      border-radius: 999px;\r\n      padding: 2px 10px;\r\n      margin-bottom: 8px;\r\n      color: var(--g900)\r\n    }\r\n\r\n    #housing-map-component .ctl {\r\n      display: flex;\r\n      flex-wrap: wrap;\r\n      align-items: center;\r\n      gap: 8px 12px;\r\n      margin: 10px 0\r\n    }\r\n\r\n    #housing-map-component .ctl .lbl {\r\n      font-family: var(--display);\r\n      font-size: 14px;\r\n      font-weight: 600\r\n    }\r\n\r\n    #housing-map-component .sens span i {\r\n      display: inline-block;\r\n      width: 9px;\r\n      height: 9px;\r\n      border-radius: 50%;\r\n      margin-right: 5px;\r\n      border: 1.5px solid #2F3E5A\r\n    }\r\n\r\n    \/* Full-width map with metro selector + rate detail below, matching the reference layout *\/\r\n    #housing-map-component .map-layout {\r\n      display: block;\r\n    }\r\n\r\n    #housing-map-component .map-main {\r\n      min-width: 0;\r\n    }\r\n\r\n    #housing-map-component .metro-panel {\r\n      margin-top: 26px;\r\n      padding-top: 24px;\r\n      border-top: 1px solid var(--g300);\r\n      display: grid;\r\n      grid-template-columns: minmax(260px, .72fr) minmax(520px, 1.55fr);\r\n      gap: 32px;\r\n      align-items: start;\r\n    }\r\n\r\n    #housing-map-component .metro-chooser h2 {\r\n      font-family: var(--display);\r\n      font-size: 22px;\r\n      line-height: 1.1;\r\n      margin: 0 0 14px;\r\n    }\r\n\r\n    #housing-map-component .metro-chooser label {\r\n      display: none;\r\n    }\r\n\r\n    #housing-map-component .metro-chooser select {\r\n      width: 100%;\r\n      min-height: 48px;\r\n      font-size: 16px;\r\n    }\r\n\r\n    #housing-map-component .metro-panel .card {\r\n      margin: 0;\r\n      padding: 20px 24px;\r\n      border-color: var(--g300);\r\n      display: grid;\r\n      grid-template-columns: minmax(260px, .9fr) minmax(390px, 1.25fr);\r\n      gap: 22px;\r\n      align-items: center;\r\n    }\r\n\r\n    #housing-map-component #cardbody {\r\n      display: contents;\r\n    }\r\n\r\n    #housing-map-component .metro-panel .card-main {\r\n      padding-right: 22px;\r\n      border-right: 1px solid var(--g300);\r\n    }\r\n\r\n    #housing-map-component .metro-panel .eyebrow {\r\n      font-size: 12px;\r\n      line-height: 1.2;\r\n      text-transform: uppercase;\r\n      letter-spacing: .02em;\r\n      color: var(--g600);\r\n      font-weight: 700;\r\n      margin-bottom: 8px;\r\n    }\r\n\r\n    #housing-map-component .metro-panel .big {\r\n      margin: 0;\r\n      font-size: 20px;\r\n    }\r\n\r\n    #housing-map-component .metro-panel .big .wait-value {\r\n      display: block;\r\n      font-family: var(--display);\r\n      font-size: 20px;\r\n      line-height: 1.05;\r\n      font-weight: 700;\r\n      color: #111;\r\n    }\r\n\r\n    #housing-map-component .metro-panel .big small {\r\n      margin-top: 7px;\r\n    }\r\n\r\n    #housing-map-component .metro-panel .facts3 {\r\n      display: none;\r\n    }\r\n\r\n    #housing-map-component .rate-detail .cap {\r\n      margin: 0 0 8px !important;\r\n      text-align: center;\r\n    }\r\n\r\n    #housing-map-component #rb {\r\n      min-height: 88px;\r\n    }\r\n\r\n    #housing-map-component #map {\r\n      width: 100%;\r\n    }\r\n    \r\n    #housing-map-component #map svg path { opacity: 65%; }\r\n\r\n    @media (max-width:800px) {\r\n      #housing-map-component .metro-panel {\r\n        grid-template-columns: 1fr;\r\n        gap: 18px;\r\n      }\r\n\r\n      #housing-map-component .metro-panel .card {\r\n        grid-template-columns: 1fr;\r\n      }\r\n\r\n      #housing-map-component .metro-panel .card-main {\r\n        border-right: 0;\r\n        border-bottom: 1px solid var(--g300);\r\n        padding: 0 0 18px;\r\n      }\r\n    }\r\n\r\n    @media (max-width:640px) {\r\n      #housing-map-component .metro-panel .card {\r\n        padding-top: 12px;\r\n        padding-bottom: 12px;\r\n      }\r\n      #housing-map-component { font-size: clamp(13px, 3.8vw, 16px); }\r\n      #housing-map-component h1 { font-size: clamp(22px, 7vw, 26px) !important; }\r\n      #housing-map-component .card label,\r\n#housing-map-component .ctl .lbl,\r\n#housing-map-component .pill,\r\n#housing-map-component .legend,\r\n#housing-map-component .metro-select-button,\r\n#housing-map-component .metro-select-option,\r\n#housing-map-component .cap,\r\n#housing-map-component svg text {\r\n        font-size: clamp(13px, 3.6vw, 15px);\r\n      }\r\n      #housing-map-component .big,\r\n#housing-map-component .metro-panel .big,\r\n#housing-map-component .metro-panel .big .wait-value {\r\n        font-size: clamp(18px, 5.8vw, 22px) !important;\r\n      }\r\n    }\r\n\r\n<\/style><\/p>\r\n<div id=\"housing-map-component\"><main class=\"wrap\">\r\n<section style=\"padding-top: 40px;\" aria-labelledby=\"sm\">\r\n<h2 id=\"sm\" style=\"font-size: 26px; padding-bottom: 0; border-bottom: 0; margin: 0 0 10px;\">Where Housing Costs Could Return to \u201cNormal\u201d Sooner<\/h2>\r\n<div class=\"map-layout\">\r\n<div class=\"map-main\">\r\n<p id=\"map-txt\" class=\"prose\">\u00a0<\/p>\r\n<div class=\"ctl\"><span class=\"lbl\">Mortgage rate<\/span>\r\n<div class=\"pills rate-pills\" role=\"group\" aria-label=\"Mortgage rate\">\u00a0<\/div>\r\n<\/div>\r\n<div class=\"legend catleg\">\u00a0<\/div>\r\n<div id=\"map\" class=\"chart\" role=\"img\" aria-label=\"Map of U.S. metros colored by how long each takes to get back to its 2018 payment-to-income share at the selected mortgage rate.\">\u00a0<\/div>\r\n<p id=\"map-sel\" class=\"cap\">\u00a0<\/p>\r\n<\/div>\r\n<!-- <h2 id=\"s3\" style=\"margin-top:14px\">What about my metro?<\/h2> -->\r\n<div id=\"card\" class=\"card\" aria-live=\"polite\"><label id=\"msel-label\">Choose a metro (or click a circle above)<\/label><select id=\"msel\" class=\"metro-native-select\" tabindex=\"-1\" aria-hidden=\"true\"><\/select>\r\n<div class=\"metro-select-wrap\">\u00a0<\/div>\r\n<div id=\"cardbody\">\u00a0<\/div>\r\n<\/div>\r\n<\/div>\r\n<\/section>\r\n<\/main><\/div>\r\n<p><script>\r\n  (() => {\r\n    const MAP = { \"nation\": \"M648,492.4L648,492.4L648,492.4ZM780.1,466.1L782.8,475.7L787,486.3L794.9,499.6L806.1,513.6L805,517.2L806.6,521.9L811.5,529.2L820,544.3L822.1,549L822.8,554.1L823.8,574.5L821.8,573.7L820.9,579.6L822.4,582.7L821.6,586.6L819.1,589.4L819.7,586.1L806.9,589.7L804.7,587.7L805.3,585L800.6,577.8L793.1,574.2L791.9,575.4L788.8,570.5L787.5,566L785.5,563.3L780.2,559.3L781.3,555.4L780.4,552L778.5,553.2L779.5,555.9L777.5,556.8L769.4,545.8L766.5,542.9L771.2,533.2L764.7,529.8L764.2,531.7L767.4,534.3L764.7,537.1L761.9,533.4L761.9,527.3L763.6,520.7L763.6,517.1L761.3,511.5L761.9,510.2L758.9,505.1L755,504.9L749.9,500L746.1,498.3L745.3,494.9L742.8,494.1L740.3,490.6L732.2,486.7L727.4,487.4L725.8,489.7L726.4,492.2L722.7,492.3L716.3,497.4L711.8,498L713.7,500.8L710,499.1L707.4,499.8L708.1,496.6L705.7,493.6L695.8,488.4L688.8,486.2L680.7,485.8L663.7,490.1L657.6,491.8L659.1,490.2L655.9,487.8L654.8,482.2L653.4,481.7L651.6,490.2L646.5,489.5L644.4,490.8L636.2,489.7L626.7,493.6L624.8,495.4L619.2,498.9L619,501.1L623.8,502.3L625.7,497.6L628.2,501.3L630,499.3L630.5,502.2L628,503L627.3,507.2L622.8,507.8L622.5,509.6L626.3,513.4L632.4,513.8L634.9,517.8L634.2,521.8L631.8,519.9L628.8,523.6L630.6,519.2L623.8,515.7L619.8,515.3L612.6,521.1L609.8,516.3L608.1,515.4L605.1,517.7L601,522.8L597.9,519.8L595.2,519.8L590.3,517.5L591.6,516L589.6,512.7L587.7,513.7L584.2,507.9L579.1,508.6L578.8,505.8L574.5,508.2L574.8,511L570.6,513.1L564.8,512.1L553.6,508.2L547.7,508.4L540.6,510.4L534.8,511.5L522.4,517.4L526.7,513.3L522.1,514.3L523.7,509.2L518.4,510.5L517.4,513.8L519.6,515.1L519.9,519.4L523.1,518.8L517.3,523.1L510.3,529.5L492,539.2L480.2,548.4L475,555.4L470.5,566.2L470.2,574.1L473.5,587.2L469.7,576L469.6,568.9L471.6,560.1L480.4,546.2L486,542.5L489.2,541.4L489,539.7L484.3,542L482.5,539.9L482,544L479.3,546.3L477.2,544.8L473.6,547L477.4,546.5L474.1,553L468.7,551.8L472.7,555.8L469.4,564.3L467.3,563.3L465,566L469.2,565.7L467.9,575.9L469.1,583.2L471.8,588L471.5,591.7L474.3,592.7L474.4,595.1L466.7,596.6L464.1,593.5L461,592.5L451.8,592.1L447.4,588.6L443.2,588.2L440.1,585.1L434.3,583.6L431.4,574.1L427.9,569.9L428.5,566.5L427.1,564.3L427.6,559.5L425.3,555.9L423.3,555.5L419.9,552.1L419,548L416.1,544.1L412,540.8L410.2,533.9L405.9,526.5L405.1,521.7L403,518.5L399,515.5L398.1,513.5L394.2,511.5L393.3,508.7L390.3,505.7L385.8,505.8L377.8,504.7L373.4,502.6L372.4,504.9L366.4,505.5L363.7,509.9L361,517.2L359.4,517.5L356.8,521.6L353.5,521.4L348.3,517L337.2,510.9L330,503.6L327.7,497.8L328.1,492L325.2,486.9L324.8,482.8L323,479.9L315.8,475.4L312.3,469.8L309.2,467.6L306.2,462.7L301.7,459.9L299,453.5L296.3,452.1L264.2,448.2L262.9,458.4L246.8,456.3L208.2,450.5L171.7,429.5L142.3,411.7L145.1,407L99.5,401.7L97.9,398.1L98.9,393.6L98.7,387.7L96.5,382.3L92.5,376.4L86.4,369.8L82.1,369.3L83,367.2L81.7,362.5L76.4,361.8L69.7,356.7L69.3,354.1L65,349.3L59.3,347.9L54.9,345.2L48.8,344.3L46.5,340.5L48.6,334.9L47.9,333.2L49.8,328.1L45.9,324.6L47.3,321.2L45.3,319.3L43.4,314.7L41.6,313.3L37.2,299.7L34.8,296.1L36.3,289L38.1,289L39.7,285.9L38.3,281.4L35.1,281L31.4,274.3L32.5,270.7L31.6,266.5L33.3,260.8L35.3,260.7L34.7,265.7L38.5,270.1L38.6,265L36.4,261.7L37.3,259L35.8,257.2L39.2,256.3L37.6,253.4L35.7,253.7L35.7,256.7L33.2,259.9L29.4,254.6L26.2,253.5L28.3,250.3L27.1,243.1L24.4,239.5L20.1,229L21.6,226.9L21.4,219.4L23.6,215.7L24,209.2L21.6,201.9L18.5,197.2L18.9,193.1L26.6,183.8L27.7,178.9L31.3,172.9L31.6,165.8L30.4,164.4L32.6,160L31.1,156.7L32,149.2L33.6,146.5L32.8,139.9L38.4,131.4L42.7,125.8L45.4,120.7L52.7,102.7L53.7,98.9L57.2,92.5L61.1,81.4L62.2,75L63.6,73.9L63.8,68.9L65,70.6L71,70.2L68.6,68.5L65.9,69.2L64.1,65.2L65.5,59.8L69.7,59L65.9,57.2L66.8,54.6L70.7,53.9L66.9,51L67.4,44.9L66.6,43L67.6,34.1L65.7,30.2L66.5,23.5L69.7,20.2L76.9,27.1L81,28.3L90.2,33L94.6,32.9L94.7,38.5L92.2,41.5L88.1,44.1L91.8,43.1L96.6,39L95.1,46L92.4,52.4L96.1,51.9L96.1,46.6L98,41.7L101.7,37.6L101.2,31.4L98,25.3L100.9,27L102.4,20.9L99.4,19.2L98.8,15.5L100,14.4L114.6,18.6L140.1,25.4L166.5,31.9L183.6,35.9L198.1,39L232.9,46.1L265.4,51.9L302.8,57.5L340.4,62.1L376.9,65.7L404.4,67.8L420.8,68.8L444.9,69.8L479.5,70.7L510.8,70.8L510.7,62.2L515.6,64.1L517.9,73.3L517.8,75.7L520.2,77.3L530.7,78.8L531.5,81.3L536.5,80.5L540.3,78.2L547.7,80.3L551.1,82.2L553.4,87.3L554.9,84.2L558.1,84L558.9,86L563.4,87.3L565.8,90.7L570.6,89.6L576.7,85.8L578.1,88.9L587.6,88L591.8,90.6L596.2,90.7L590.4,94.2L582,97.5L579,99.5L572.7,105.7L569.9,109.4L558.9,119.4L560.2,121.1L563.3,121.4L568,119.6L576.2,115.1L579.7,116.1L577,122.8L580.2,120.5L585.3,123.1L591.3,120.2L593.9,117.3L600.5,115.8L612.9,105.8L615.1,102.8L618.6,100.5L624.4,99.7L625.9,101.1L622.2,101.8L615.4,111.8L615.6,116.6L618.6,112.5L624,112.5L628.3,114.3L633.1,120.8L643.6,121.4L651.8,115.2L662,114L665.3,111.9L670,111L669,112.8L669.8,117.6L676.5,118.3L679,116.1L683.4,114.8L684.4,121.5L682.5,123L686.1,123.6L687,125.7L690.2,126.2L690.2,124L693.2,123.6L695.2,125.8L694,127.6L685.2,127.2L681.2,128.8L676.5,126.6L674.2,131L671.2,128.3L665.8,126.9L662.9,127.3L660.9,130.3L657,131.8L652.6,131.3L650.3,135.5L645.2,139.2L647.6,134.3L643.6,134.5L643,137.7L637.9,139.6L632.4,153L632,155.7L629,156.6L626.2,165.6L628.1,166.1L630.6,163.3L632.6,158.9L635.7,157.1L637.9,150.9L641.6,147.6L641,152.4L639.1,155.3L635.5,165.1L634.7,170.5L635.4,173.6L633.5,175.5L632.5,180.8L633.4,185.3L630.8,195.3L631.7,200.9L634.1,205.7L634.2,212.4L634,216.8L637.2,222.1L640.3,229.8L642.2,231.2L647.2,230.6L651.9,227.6L655.1,224.2L658.6,215.5L659.8,210.2L660,204.7L658.9,198L652.4,184.3L653.8,180.1L651.9,174.9L655.2,168L654.9,159.7L657.1,158.7L657.1,154.9L661.1,153.5L663.6,147.8L664.7,150.6L663.9,155.9L665,157.2L666.3,152L666.1,157.6L667.8,153.2L667.3,145.5L669.6,143.3L674.2,141.9L670.5,138.3L672.9,134.4L675.4,132.7L680.6,135.2L684.6,135.1L686.6,137.8L689.8,137.9L695.1,140.2L696.7,139.9L701.3,147L699.1,146.2L698.8,148.9L700.9,150.3L702.2,153.9L702.4,162.5L699.2,165.5L699.2,169.4L695.4,171.5L694.1,177.1L699,180.2L704.6,170.6L709.9,167.7L713,169.1L715.2,172.4L718.9,185.8L721.5,191.2L720.9,199.8L718.5,202.2L719.1,198.7L716.3,199.4L715.1,206.5L712.1,209.5L712.5,212.9L709.8,217.1L708.3,221.7L717.6,225.5L719,223.6L725.6,227.4L733,223.3L737.5,223.2L741.9,218.3L748.5,213L756.1,209L767.5,200.3L776.4,192.1L777.5,188.8L780.3,186.1L776.9,182L774.8,176L783.8,171.7L792.1,170.3L795.8,170.3L799.8,171.9L801.9,170.5L809,169.2L812.8,166.7L816.6,161.8L819.3,161.1L819.4,158.3L816.9,154.4L819.3,152.5L814.7,150.3L815,146.4L820.5,141.3L822.1,137.5L828.6,127.9L832.3,124.4L857.6,117.8L873.7,114L886.1,110.5L886.7,105.2L890.9,102.4L891.8,101.2L894.8,103.1L894.3,99.3L897.3,98.8L895.2,96.3L898.2,88.9L900,86.1L898.2,82.6L898.1,76.3L899.9,73.8L899.1,67.5L905.9,47.6L908.8,47.5L910,51.5L912.5,52.3L916.6,48.5L922.6,45.8L929.7,49.7L937.3,74L938.7,79.9L944.6,79.9L946.3,84.3L946.1,87L949.5,89.7L952,88.1L957.1,94.8L955,99.4L952.8,98.6L950.2,105.6L946.8,104.3L946.3,107.5L943.8,110.3L941.4,109.7L941.5,112.5L938.9,113.7L937.3,109.7L935.4,111.3L937.1,114.9L935.7,117.6L934.5,114.7L931.9,114.3L931,110.1L929.7,113.9L929.3,120.5L930.2,122.9L921.4,128.9L919.7,132.2L918.1,130.9L915.1,134.1L915.1,136.8L912.8,138.3L912.9,142.3L911.2,143.3L910.2,150.1L909.5,154.8L911.2,158.5L914.3,158.4L909.5,165.8L910,168.6L911.8,167.4L917,171.2L917.5,174.2L919.4,174.4L921.7,177.7L924.8,177.5L928.8,175.1L928.4,171.4L931,176.8L924.7,179.9L920,183.3L917.9,179.4L915.4,182.8L915.4,185.3L912.8,186.4L909.1,188.3L910.5,182.8L909.8,182.1L906.7,181.2L908.1,188.2L907.6,190.7L901.7,193.5L900.2,193.5L893.5,197L884.7,198.4L881.9,202.6L878.1,204.5L874.1,208.3L873.7,211.1L877.1,208.5L881.6,208.4L882.7,206.7L890.9,204.2L894.7,199.6L899.5,201.8L895.5,205.8L885.7,213L871.7,219.3L869.2,216.4L868.9,217.2L868.5,220.3L866.6,221.4L871.4,222.4L872.4,224.8L873.2,237.3L869.9,247.5L867.1,250.9L865.4,257.2L862.6,259L862.9,253.8L858.6,254.3L851.7,249.8L849.4,246.3L849.2,245.8L849.7,244.4L848.5,246.7L849.3,249.3L852.8,252.5L854.8,258L859.5,262.9L861.3,262.7L863.7,270.3L862.5,280.5L861.3,284.3L858.4,288.2L858.5,292.2L856.6,301.3L854.5,303.5L852.6,300.7L852.9,292.1L855.6,285.9L855.5,283.8L851.6,285.3L851.2,281.8L847.7,275.6L847.6,278.7L845.5,279.1L841.4,274.1L841.8,271.3L846.4,271.3L840.1,267.8L842.3,266.1L841,263.7L839,266.3L839.4,261.7L841.4,262.9L841.8,260L839.3,259.3L841.6,251.7L839.2,254.5L836.8,254L837.6,257L836.3,258.8L838,262.7L836.2,268.8L838.1,274L841,276.7L840.3,279L843.8,284.2L838.3,281.3L834.6,281.5L829.9,277.6L827,280.2L825.3,277.7L827.1,273.8L824.5,278.6L825.8,280.9L829.6,278.7L831.9,282.6L838.2,282.9L840.5,285.2L845.9,287.3L845.1,289.3L846.2,293.2L842.2,294.6L846.4,294.9L848,300.4L845.1,298.9L846.3,302.8L849.2,305.5L847.3,308.7L840.1,304.8L846.3,310.5L848.8,308.5L854.9,308.3L858.8,315.9L860.6,320.1L865.6,328.4L864,327.6L859.7,318.9L855.9,317.5L862.1,325.5L859.3,323L857.3,325.6L850.8,329.9L846.8,330.3L847.4,332.7L856,329.6L859,329.8L858.7,332.3L860.4,336.2L860.2,329.5L863.3,329.4L865.7,334.5L865.1,337.3L863,337.9L859.7,344.3L851.6,344.7L843,343.4L854.2,347.1L852.7,351.9L849.6,355L850.5,355.8L854.8,352.9L859.2,353.5L857.1,355.8L855.9,360.3L862.3,349.7L862.9,350L855.6,361.2L851.4,360.8L843.8,364.5L838,370.5L834.5,376.7L833.4,384.1L829.2,383.5L822.8,386.1L819.8,387.9L816.3,392.2L813.5,398.8L813.6,403.6L811,407.9L807.9,407.8L807.4,410.8L802.9,415.1L800.6,419L794.9,422.9L792.1,423.7L793,426.9L790,428.8L786.1,434.6L786.8,436L782.6,442.3L782.4,447.1L781,450.8L781.3,454.4L778.3,460.2L780.2,461.5ZM104.2,551.6L104.2,551.6L104.2,551.6ZM99.1,569.9L103.4,565.2L104.7,569.1ZM95.1,582L95.1,582L95.1,582ZM93.3,575.4L99,570.7L103.1,570.6L104.5,573.3L99.2,577.7L97.5,577.1L95.4,579.6ZM93.1,582.8L93.1,582.8L93.1,582.8ZM88.9,587.6L88.9,587.6L88.9,587.6ZM71.8,593.7L71.8,593.7L71.8,593.7ZM71,591.7L71,591.7L71,591.7ZM68.2,593.5L68.2,593.5L68.2,593.5ZM65.8,590.1L65.8,590.1L65.8,590.1ZM67.5,564.2L67.5,564.2L67.5,564.2ZM60.9,591.2L60.9,591.2L60.9,591.2ZM58.3,592.4L58.3,592.4L58.3,592.4ZM66.1,523.9L66.1,523.9L66.1,523.9ZM56,595.9L56,595.9L56,595.9ZM44.6,597L44.6,597L44.6,597ZM42.7,595.5L42.7,595.5L42.7,595.5ZM40.6,595.9L40.6,595.9L40.6,595.9ZM44.2,547.5L49.5,546.7L51,551.2L48.4,552ZM31.3,600.8L33.8,600.4L37.9,596.5L40.1,598.2L36.6,600.9ZM50.7,504.5L59.1,500.7L65.7,499.7L65.2,503.9L71.4,505.1L74.3,503.7L71.2,500.1L73.2,497.6L66.5,495.6L66.2,492.3L59.4,484.4L61.5,480.8L67.2,481.3L70.5,478.8L71.7,475L75.3,471.4L78.2,471.3L81.6,468.4L86.7,468.1L89.3,465.1L94,466.3L95.1,468.8L101.4,468.7L100.9,470.4L104.4,472.1L108.9,470.8L113.9,472.8L118.9,472.6L122.5,473.6L126,471.7L132.9,474.4L147.3,547.1L151.4,547.2L154.5,545.1L154.6,547.3L162.3,552.4L163.6,554.8L166.5,551.9L166.5,548.3L169.4,545.9L176.3,551.6L180.8,553.8L192.6,565.1L200.1,566.6L202.9,572.6L201.4,577.9L198.4,575L192.8,573.7L192,570.8L189.2,570.5L190.2,568.2L188.2,565.4L183.2,563.5L182.3,560.4L178.3,556.5L176.2,556.8L171.4,551.2L174.7,557.7L172.7,556.5L168,559.3L160,554.5L153,552.1L153.3,549.3L151,551.3L140.6,550L135.3,551.6L125.7,547.4L124.5,545.3L119.2,547.1L121.1,552.9L119.4,554L113.5,554.3L111,558.8L108.8,560.4L104.8,560.3L106.7,557.2L105.2,556.2L106.9,552.5L106.7,548.5L111.9,544.1L109.4,543.8L106.3,546.2L101.7,553L100.5,556.9L96.2,559.5L96,562.3L99.6,563.5L96.4,566.5L95.8,569.3L89.9,572.1L85.8,575.8L85.6,578.3L82,579.1L76.7,585.8L70.6,586.4L67.3,589.1L62.9,589.5L60.6,591.3L55.6,591.9L52.7,594.2L46.7,594.8L48.6,591.6L56.5,590.1L62,585.2L66.5,584.6L75.2,578.8L80.9,573.4L82.4,566L83.9,563.2L79.6,565L78.1,563.5L75.7,566.2L73.3,562L71,560.7L64.7,564.1L64.2,558.3L65.8,556.7L63.8,551.1L63.1,552.3L58.4,553.3L52.6,546.7L54.4,545.2L51.1,538.2L53.7,533.6L60.7,525.7L64.3,527.9L67.5,524.9L71.5,525.2L73,523.2L71.5,517L73.7,515.7L72.5,514L67.6,516.5L61.3,515.5L59.1,516L55.2,514.1L53.9,509.4L55.9,508.7ZM24.9,603L29.3,599.1L31.4,600.2ZM28.5,573.3L28.5,573.3L28.5,573.3ZM20.4,602.1L20.4,602.1L20.4,602.1ZM26.8,568.3L26.8,568.3L26.8,568.3ZM16,603L16,603L16,603ZM34.4,517.8L39.4,517.9L42,521.2L44.8,522.4L40.8,524.5L39,521.1ZM7,603L7,603L7,603ZM23.4,539.9L23.4,539.9L23.4,539.9ZM0,602.3L0,602.3L0,602.3ZM-6.2,601.2L-1,601.3L-2,602.5ZM-10.2,599.4L-10.2,599.4L-10.2,599.4ZM-11,601.2L-11,601.2L-11,601.2ZM-15,601.4L-13.1,599.3L-11.1,600.8ZM-18.1,599.6L-18.1,599.6L-18.1,599.6ZM-20.1,597.5L-20.1,597.5L-20.1,597.5ZM-30.2,592.5L-30.2,592.5L-30.2,592.5ZM-35.2,593.3L-35.2,593.3L-35.2,593.3ZM-40.8,588.6L-40.8,588.6L-40.8,588.6ZM-55.8,576.6L-55.8,576.6L-55.8,576.6ZM-57.6,570.9L-57.6,570.9L-57.6,570.9ZM197.4,577.4L197.4,577.4L197.4,577.4ZM196.5,577L196.5,577L196.5,577ZM194.7,574.9L194.7,574.9L194.7,574.9ZM187.6,570L187.6,570L187.6,570ZM187.1,569.1L187.1,569.1L187.1,569.1ZM186.2,576.9L186.2,576.9L186.2,576.9ZM184.9,573.1L185,569.8L186.8,569.5L194.4,575.9L195.7,580.1L189.6,577.5ZM182.7,574.2L182.7,574.2L182.7,574.2ZM180.3,567.2L181.9,564.6L185.4,564.3L188.7,566.7L183.9,568.5L182.7,572.5ZM174.6,555.9L178.2,556.9L181.4,562.6L179.6,565.9L178.9,563.1ZM173.9,564.8L177.2,563.4L180.8,572.7L176.1,568.4ZM173.5,565.9L173.5,565.9L173.5,565.9ZM169.1,561.4L171.5,557.8L175.3,558.7L177.1,562.7L173.7,562.2L173.8,564.8ZM120.8,555.1L123.6,550L124.3,550.9ZM120.4,551.4L120.4,551.4L120.4,551.4ZM814.6,594.5L814.6,594.5L814.6,594.5ZM812.7,596L812.7,596L812.7,596ZM807.4,599L807.4,599L807.4,599ZM793.7,604.2L794.8,602L800.5,597.7L805.1,600.1L796.4,604.1ZM789.6,605.4L789.6,605.4L789.6,605.4ZM786.4,605.5L786.4,605.5L786.4,605.5ZM777.4,558.3L777.4,558.3L777.4,558.3ZM770.3,606.2L770.3,606.2L770.3,606.2ZM720.4,495.2L720.4,495.2L720.4,495.2ZM939.8,114.8L939.8,114.8L939.8,114.8ZM938,116.1L938,116.1L938,116.1ZM936.3,112.2L936.3,112.2L936.3,112.2ZM932.2,116L932.2,116L932.2,116ZM933.5,124.7L933.5,124.7L933.5,124.7ZM931.4,119.2L932.7,117.1L934.3,119.4ZM930.1,116.3L930.1,116.3L930.1,116.3ZM927.7,186.8L933,186L930.7,187.6ZM918.3,188.3L921.3,184.4L924.6,186.7ZM864.3,329.1L864.3,329.1L864.3,329.1ZM863.3,349.7L863.6,348.4L870.9,343.2ZM77.2,374.3L81.1,376.9L81.3,379.5L78.9,378.3ZM74.7,384.2L78.3,390L75.8,388.7ZM65.2,358.4L65.2,358.4L65.2,358.4ZM58.3,374.3L58.3,374.3L58.3,374.3ZM56.6,355L62.6,358.5L56.9,357.4ZM50.3,355.2L54.1,355.4L54.9,357.8L51.9,358.1ZM46.8,353.4L46.8,353.4L46.8,353.4ZM305.4,584.5L308.2,581.9L310.3,577.9L308.8,575.4L309.7,572.5L315.2,575.8L322.2,578.3L326.3,582L326.1,584.5L332.1,589.1L328.6,593.3L321.8,595.2L317.3,598.1L313.8,603.1L309.3,600.5L308.4,598.5L309.1,593.7ZM291.6,557.9L293.9,555.4L296.4,558.5L299.6,557.2L306.5,560.7L305.4,563.9L298.5,565.6L296.7,561.1L293.1,560.2ZM291.6,566.7L294.3,565L294.8,567ZM283.9,558.3L287.4,558L289.3,560.4L285.9,562.1ZM278.6,553.8L279.6,551.2L291.4,552.6L287.8,555L283.2,553.8ZM258.1,543L261.3,542.9L264.7,540L267.3,545.8L271.4,549.4L261.5,549.3ZM226.1,532.6L227.3,530.2L231,528.2L235.4,528.6L236.6,530.2L235.8,534.3L233.3,536.3L230,535.8ZM216.3,536.6L220,533.2L219.8,535.6L217.1,538.2ZM848.9,285.2L849.9,285L848.9,285.2ZM847.3,281.5L847.3,281.5L847.3,281.5ZM677.1,130.8L682,132.3L679.8,133.4ZM663.5,133.2L663.5,133.2L663.5,133.2ZM662.4,138.6L663,134.8L664.5,138.1ZM660.9,135.6L660.9,135.6L660.9,135.6ZM658.8,142.5L658.8,142.5L658.8,142.5ZM656.6,149.6L656.6,149.6L656.6,149.6ZM655.4,152.7L655.4,152.7L655.4,152.7ZM645.5,139.9L645.5,139.9L645.5,139.9ZM601.4,92.4L610.1,85.8L613.6,84.4L610.1,88.7L603.5,93.3ZM644.5,493.1L644.5,493.1L644.5,493.1ZM639.3,492.9L639.3,492.9L639.3,492.9ZM635.2,493.9L635.2,493.9L635.2,493.9ZM631.8,493.6L631.8,493.6L631.8,493.6ZM899.3,195.7L899.3,195.7L899.3,195.7ZM896.7,198L896.7,198L896.7,198ZM815.5,153.8L815.5,153.8L815.5,153.8ZM814.2,153.7L814.2,153.7L814.2,153.7ZM720.6,223L720.6,223L720.6,223ZM718.7,222.3L718.7,222.3L718.7,222.3ZM93.2,50.9L93.2,50.9L93.2,50.9ZM98.7,24.6L98.7,24.6L98.7,24.6ZM98.9,20.3L98.9,20.3L98.9,20.3ZM97.7,23.5L97.7,23.5L97.7,23.5ZM95,31L98.5,27.7L99.3,30.6L95.8,31.6L97.4,32.7L97.1,36.6L99.6,36.9L98.1,39.2L96.2,36L96.8,33.1ZM95.5,17.9L95.5,17.9L95.5,17.9ZM93.6,19.6L93.6,19.6L93.6,19.6ZM95.2,13L95.2,13L95.2,13ZM91,21.2L95.9,19.9L98.1,21.8L95.6,26.6L91.6,23.8ZM91.1,19.1L91.1,19.1L91.1,19.1ZM641.9,146.3L641.9,146.3L641.9,146.3ZM635.6,150.4L635.6,150.4L635.6,150.4ZM584,113.3L584,113.3L584,113.3ZM583.2,116.2L583.2,116.2L583.2,116.2ZM581.2,115.2L581.2,115.2L581.2,115.2ZM579.6,117.2L579.6,117.2L579.6,117.2ZM579.8,114.4L579.8,114.4L579.8,114.4ZM579.1,113L579.1,113L579.1,113ZM579.2,118.5L579.2,118.5L579.2,118.5ZM576,114.1L576,114.1L576,114.1ZM637.5,497.4L637.5,497.4L637.5,497.4ZM628.5,497.9L631.3,495.4L631.8,498ZM623.7,510.2L623.7,510.2L623.7,510.2ZM609.5,520.8L609.5,520.8L609.5,520.8ZM606,521.3L606,521.3L606,521.3ZM602.2,522.4L602.2,522.4L602.2,522.4ZM576.5,511.5L579.9,510.5L582.6,512L580,513.9ZM908.4,188.4L908.4,188.4L908.4,188.4ZM906.7,196L906.7,196L906.7,196Z\", \"borders\": \"M646.5,489.5L642.2,455L643.7,386.2L641.6,384L641.5,383.7L666.5,381.9L689.2,379.7L702.5,427L704.5,431.8L707.8,436.5L707.3,438.8L709.6,439.9L706.7,443.2L705.9,450.1L708.6,457.1L708.3,464.8L710.8,468.8L693.6,470.8L660.7,474L660.3,477L665.1,480.9L664.6,484.7L666.3,486.4L663.7,490.1M145.1,407L148.8,406.9L150.7,403.8L150.2,400.8L147.4,400.1L148,391.7L152.4,389.1L153.9,380.5L156.8,377L160.1,376.1L162.8,373.6L159.8,369.6L158.5,363.2L156.2,359.1L156.8,356.2L158.7,352.8L158.2,345.1L159.9,331.1L166.6,331.5L170,334.9L172.6,331.5L176,313.3L202.7,318.2L239.3,324.3L264.6,328L246.8,456.3M710.8,468.8L714.2,475L765.8,471.5L767.3,476L769.7,475.6L769.8,471L768.7,466L770,464.9L774.7,466.1L780.1,466.1M689.2,379.7L712.7,376.8L734.9,373.7L731.6,379L731.7,381.5L738.4,385.4L741.1,385.4L744.4,391.4L748.3,396.2L754.5,399.6L756,402.3L761.9,405.8L765.7,411.8L771.3,414.4L773.2,418.1L774,422.7L776.8,424L780.8,429.6L781.4,433.4L786.1,434.6M651.9,227.6L685.6,223.9L685.8,225.4L688.5,247.4L692.5,284.3L691.3,285.7L693.5,291.6L690.7,292L687.5,294.5L685.1,293.5L682.6,295L683.6,298.2L680.6,301L680,303.7L677.1,305.1L675.6,311.5L674.1,312.8L669.8,310.7L668.6,308.8L665.5,311.7L666,314.3L662.3,315.5L660.8,313.4L656.7,315.7L655.3,318.7L650.6,315.6L646.7,315L645.3,316.8L639.4,319.7L638,317.4L641.5,308.6L646.5,298.5L646.2,292.6L643.5,287.9L645,283.6L640.3,229.8M393.9,271.9L422.4,273.5L443.4,274.3L464.8,274.9L495.4,275.2L509.9,275.2L511.7,277.3L516.5,277.7L516.1,281L513.5,285.8L516.5,289.2L518.5,293.2L522.3,295.2L522.8,343.8L489,344L458.9,343.6L432.6,342.7L389.7,340.7M910.2,150.1L904.5,145.2L894.8,114.3L890.9,102.4M909.8,182.1L907.7,181L905.5,176L898.8,178L898.7,177.7L871.2,184L870.6,183.2L871,167.6L884.1,164.8L902.9,160.7L906.2,156L909.5,154.8M912.8,186.4L910.5,182.8M560.2,121.1L555.9,122.3L556.4,135.6L551.1,139L547.6,144L547.5,147.3L551.3,149.9L549.7,153.6L550.1,161.7L549.3,165.9L553.6,169.9L556.9,170.1L559.2,172.7L563.9,174.9L564.9,177.8L569.8,181.6L572.3,182.2L575.6,187.1L576.3,193.3L549.3,194.4L515.8,195.2L490.6,195.2L490.8,154.3L487.1,151.7L484.4,147.3L488.8,142.5L489.1,139.9L488.6,130.9L486.5,127.6L485.5,123.3L484.9,102.5L480.5,89.4L480.5,80.8L481.4,77.7L479.5,70.7M849.7,244.4L850.8,241.4L854.8,238.7L854.8,236.9L860.6,230.8L853.7,226.4L852.9,223.6L850.7,223.4L849.9,220.8L851.5,216.1L849.6,213.8L855,204L870.1,209L869.2,216.4M868.9,217.2L866.9,218.3L866.6,221.4M849.4,246.3L849.2,245.8M712.7,376.8L712.6,371.4L717.3,369.2L717.4,365.9L721.1,362.5L726.3,361.7L733,355.7L734.6,355.9L737.8,350.2L740.1,348.3L740.8,350.5L747.2,345.2L750.7,345.5L752.4,341.1L755,338.2L755.5,334L780.3,331.1L812.1,325.5L840.3,320L858.8,315.9M822.8,386.1L798.1,368.4L777.6,371.5L777.5,368.8L772,364.7L749.4,367L745.6,368.2L734.9,373.7M489.1,139.9L459.9,139.4L442.8,138.8L412.5,137.3L392.2,136L371.1,134.3L376.9,65.7M522.8,343.8L523,355.3L526.8,380.5L526.4,420.7L519.6,418.5L517.3,415.7L512.4,413.5L511.4,415.4L506.5,415.4L506.2,414.2L500.9,416.3L499.2,415.2L492.8,417.9L491.6,419.8L485.4,414.7L483.8,416.6L481.1,413.8L476.1,418.5L475.4,414.4L472.3,416.6L466.5,412.7L463.1,415.8L460.9,414.6L461.7,412.5L459,412.1L457.6,409.6L454,408.5L451.7,410.6L450,408.6L446.6,409.1L442.5,406.8L438.7,406.8L438.4,404L435.3,401.1L434.9,402.9L429.4,402.6L425.9,398.5L423.9,398.3L425.8,353.9L396.5,352.4L371.6,350.8L372.5,339.4M767.5,200.3L768.6,206.4L800.4,200.6L824.8,195.6L840.9,192.1L843.2,194.8L847.1,196.5L849.7,202.4L853.9,202.5L855,204M850.8,241.4L846.5,241.9L844.9,244.6L816.4,250.5L782.3,256.9L764.5,259.9L761.1,239.2L756.1,209M490.6,195.2L488.2,195.2L488.5,199.8L490.8,203.9L489,210L487.5,211.9L490.6,218.3L488,217.9L486.6,214.7L476.4,209.5L467,209.6L465.7,211.6L457.3,207.3L456.8,206.2L434.2,205.4L403.8,203.9L365.3,201L369.1,155.7L369.4,155.7L371.1,134.3M526.4,420.7L528.8,422.8L532.1,421.6L534.8,422.5L535,434.7L535.5,457.9L539.8,463L539.6,467L541.8,469.1L545.4,476.2L546.1,479.9L545.5,484.5L542.3,490.3L543,493.3L543.1,502.1L538.9,507.5L540.6,510.4M296.3,452.1L294.3,449.3L295.1,447L317.8,449.4L338.7,451.3L362.9,453.3L364.6,429.6L366.7,407.5L370.9,350.7L371.6,350.8M365.3,201L361.4,246.5M277.1,237.2L301.1,240.3L333.1,244L361.4,246.5L395.3,249L393.9,271.9L389.7,340.7L372.5,339.4L348.5,337.6L315.3,334.3L278.8,329.9L264.6,328L268.4,301.5L268.4,299L271.2,279.7L277.1,237.2L252.7,233.7L243.5,232.2L247.2,209.6L256,153.9L257.8,142L263.3,143.1L298.3,148L339.2,153L369.1,155.7M898.8,178L902.2,191.1L901.7,193.5M874.1,208.3L872.3,206L875.7,202.6L874.1,201L871.2,184M509.9,275.2L508,273.9L507.1,270L504.7,268.1L502,261.9L521.4,262L544.8,261.3L570.9,259.6L576.5,264.7L575.1,268L575.5,272.6L578.1,279.5L584.1,285.6L589.7,289.9L591.3,297.3L593.4,298.3L594.9,296.1L601,298.4L600.1,303.2L597.6,309.3L597.6,313.2L606.1,320.6L607.1,319.7L614.1,326.4L615.2,332L613.8,333.4L618.6,340L621.2,339.9L621.8,340.3L620.2,349.5L617.4,348.6L616.9,351.3L615.6,351.5L614.7,351.5L615.4,354.2L612.1,357.7L614.2,360.1L611.9,363.1L600.2,364L605.2,357.3L603.6,352.2L586,353.2L549.9,354.7L523,355.3M782.3,256.9L784.1,268.5L787.1,265.8L789.7,261.4L792.3,261.6L794.3,257.4L800.4,258.3L801.8,255.2L804.5,253.4L807.7,255L810.8,254.4L810.6,256.4L813.9,260.3L812.9,264.9L802.6,259.1L802.9,266L796.6,276.6L794,275L790.3,285.9L787.3,285.4L783.8,282.9L783.7,286.6L780.4,294.1L780,297.5L776,305.1L777.5,306.3L776.6,309.3L773.8,311.7L773,310.5L766.7,315.4L761.3,318.1L758.5,316.2L755.4,319.7L750,318.6L746.8,314.7L747.1,313.2L740.9,311.1L733.8,301.7L733.2,294.8L737.9,293.5L739.7,289.8L738.6,285.4L741.4,279.5L744,282.7L746,281.3L744.6,277.8L746.4,273.1L748.1,273L749.8,269.4L753.7,269.4L757.8,263.4L759.8,256.2L759.5,252.5L760.9,246.6L758.9,241L761.1,239.2M576.5,264.7L577.4,259.4L581.2,257.8L581.5,254.5L583.6,251.8L583.7,247.9L580.7,244.7L581.6,240.8L588.1,239.3L593.2,236.1L593.5,232.7L595.5,231L595.5,223.8L591.6,221.6L590,218.2L587,215.4L617.8,213.7L634.2,212.4M639.4,319.7L637.3,323.1L639,327.3L632.2,329.8L631.7,332.2L633.6,335.1L631.5,337.1L624.4,334.1L620.6,337.5L621.2,339.9M611.9,363.1L613.7,365.2L612.7,367.3L608.4,369.4L610.2,371.6L607,373.8L604.7,377.7L606.6,377.6L605.4,380.6L607.1,383.2L602.8,386.7L604,388.7L598.4,393L600.4,394.3L598.2,395.7L598.6,400.4L595.3,401.6L593.4,409.4L589.7,410.8L591.8,415.1L588.9,415.5L590,418.1L587.6,420.4L587.6,423.2L589.5,425.1L590.7,429.8L588.6,430.7L589.4,433.1L569.4,433.9L535,434.7M32.6,160L46.5,164.1L62.1,168.4L83.7,174.8L100.5,179.2L83.3,245.5L156.8,356.2M863.7,270.3L852.6,272.5L844.9,244.6M576.3,193.3L576.5,196.3L579.2,198.7L577.4,201.6L578.2,206.8L579.9,210.5L585.6,212.4L587,215.4M502,261.9L499.9,258.5L501.2,255.1L498.9,244.7L499.3,241.9L496.4,239.9L496.9,234L494.2,229.6L490.6,218.3M692.5,284.3L696.4,284.6L698.5,283.2L700.9,284.9L703.9,289.8L710.2,290.2L713.9,292.7L716.2,290.6L720.6,292.4L727,287.9L728.4,291.7L733.2,294.8M747.1,313.2L741.2,320.4L735.2,324.6L733.1,330.3L729.8,331.5L729,334.2L719.8,338.7L719.6,339.2L704.2,340.8L692.5,341.5L681.1,342.8L672,343.2L644.8,346.1L640.8,345.4L641.4,349.5L616.9,351.3M615.6,351.5L614.7,351.5M849.9,285L848.9,285.2M862.5,280.5L856.1,282.7L855.5,283.8M827.1,273.8L827.9,269.9M825.9,267L827,265.3L829.7,267.2L827.9,269.9L825.9,267L818.2,264.2L818.7,261.5L813.9,260.3M708.3,221.7L685.8,225.4M632.4,153L629.9,151.5L631,147.6L627.3,147L628.2,140.2L622.3,137.4L622.7,135L611.9,133.8L606.7,131.4L590.3,128L588.3,124.2L585.3,123.1M602.8,386.7L618.9,385.7L641.6,384M624.8,495.4L622.3,493.1L621.2,489.5L617.6,484.9L619.1,477.1L582.6,479.2L584,478.2L582.2,473.1L584.6,472.8L583.3,470L585.4,470L584.4,467L586.4,466L587,461.4L592.6,453L590.4,451.5L595.4,448.2L590.9,444.9L592.9,441.6L590.1,441.4L591.3,435.6L589.4,433.1M256,153.9L253.6,151L252.6,147.6L249.9,147.5L249.6,151.1L243.3,149.7L239.3,150.1L236.7,148.5L234.6,150.9L228.5,149L227,151.6L224.8,149.1L224.1,141.5L220.8,140.7L219.4,138.5L220.2,134.2L218.6,132.3L216.1,122.8L216.9,121L214.6,118.2L208.1,122.6L205.2,119.7L205.8,114.9L208.6,113.7L208.2,109.1L210.8,101.7L213.4,96.3L208.9,95.6L204.8,89.7L205.3,88.4L200.8,79.7L198,78L195.3,74.3L196.8,68.7L193.3,61.5L198.1,39M884.1,164.8L882.3,163.4L882.1,157.6L880.6,145.8L883.1,133L881.8,127.9L885.1,126.3L887.9,122.1L885.6,116.8L886.9,112.7L886.1,110.5M857.6,117.8L859.2,127.3L860.9,129L862,134L861.3,139.5L864.1,145.3L863.6,148.6L867.1,150.3L871,167.6M71,70.2L74.2,73.9L77,73.8L79.5,76.9L80.1,80L79,86.8L86.2,91.5L93.8,89.9L98.2,90.6L102.7,93.1L102.8,94.7L112,93.9L113.8,95.4L118.4,96L122.8,94.9L130.5,94.9L136.2,95.7L138.7,94.8L170.5,102.4L172,106.8L175,109L175.7,112.3L166.8,124.1L164.2,129.1L161.9,129.9L157.4,136.6L157.9,139.9L160.9,141.2L161.7,143.3L157.9,150.5L148.9,190.7L121.6,184.5L100.5,179.2M719.8,338.7L727.2,337.9L755.5,334M176,313.3L197.7,201.1L200.2,201.7L247.2,209.6M183.6,35.9L178.3,58.7L170.8,92.4L171.3,98.5L170.5,102.4M197.7,201.1L159.6,193.2L148.9,190.7\", \"pts\": { \"San Francisco\": [34.8, 261.5], \"Boston\": [908.7, 167.1], \"San Diego\": [99.8, 397.4], \"Minneapolis\": [541.8, 161], \"Nashville\": [665.1, 355.1], \"Las Vegas\": [153, 328.1], \"Jacksonville\": [777.3, 475.2], \"Anaheim\": [91.8, 369.3], \"Baltimore\": [833, 257.2], \"Chicago\": [638.2, 226.1], \"Cincinnati\": [697.9, 283.8], \"Cleveland\": [738.4, 222.8], \"Columbus\": [721.4, 260.9], \"Detroit\": [713.1, 207.3], \"Fort Lauderdale\": [822.5, 564.4], \"Indianapolis\": [667.7, 271.7], \"Kansas City\": [522.8, 295.7], \"Milwaukee\": [631.3, 200.1], \"Montgomery County\": [850.5, 234.1], \"Nassau County\": [875.6, 213.7], \"Newark\": [866.9, 215.8], \"New Brunswick\": [863.5, 222.4], \"New York\": [869.7, 215.8], \"Philadelphia\": [854.2, 237.2], \"Pittsburgh\": [770.8, 242.2], \"Providence\": [906.2, 180.5], \"St. Louis\": [599.8, 303.5], \"Tampa\": [769.5, 530.9], \"Virginia Beach\": [855.4, 309.6], \"Warren\": [712.9, 203.2], \"West Palm Beach\": [822, 550.8], \"Charlotte\": [775.4, 362.3], \"Atlanta\": [714.9, 405.1], \"Miami\": [822.8, 572.6], \"Orlando\": [788.9, 514.5], \"Houston\": [510.4, 509.2], \"Fort Worth\": [472.8, 440.8], \"Denver\": [342.9, 273.9], \"Los Angeles\": [86.9, 363.2], \"Sacramento\": [55.6, 248.1], \"San Antonio\": [448.8, 516.4], \"Portland\": [79.4, 90.2], \"Seattle\": [97.7, 46.2], \"Oakland\": [37.6, 261.6], \"Austin\": [464, 497.4], \"San Jose\": [41.2, 273.6] } };\r\n    let D = [];\r\n    const STATE_BY_METRO = { \"San Francisco\": \"CA\", \"Boston\": \"MA\", \"San Diego\": \"CA\", \"Minneapolis\": \"MN\", \"Nashville\": \"TN\", \"Las Vegas\": \"NV\", \"Jacksonville\": \"FL\", \"Anaheim\": \"CA\", \"Baltimore\": \"MD\", \"Chicago\": \"IL\", \"Cincinnati\": \"OH\", \"Cleveland\": \"OH\", \"Columbus\": \"OH\", \"Detroit\": \"MI\", \"Fort Lauderdale\": \"FL\", \"Indianapolis\": \"IN\", \"Kansas City\": \"MO\", \"Milwaukee\": \"WI\", \"Montgomery County\": \"PA\", \"Nassau County\": \"NY\", \"Newark\": \"NJ\", \"New Brunswick\": \"NJ\", \"New York\": \"NY\", \"Philadelphia\": \"PA\", \"Pittsburgh\": \"PA\", \"Providence\": \"RI\", \"St. Louis\": \"MO\", \"Tampa\": \"FL\", \"Virginia Beach\": \"VA\", \"Warren\": \"MI\", \"West Palm Beach\": \"FL\", \"Charlotte\": \"NC\", \"Atlanta\": \"GA\", \"Miami\": \"FL\", \"Orlando\": \"FL\", \"Houston\": \"TX\", \"Fort Worth\": \"TX\", \"Denver\": \"CO\", \"Los Angeles\": \"CA\", \"Sacramento\": \"CA\", \"San Antonio\": \"TX\", \"Portland\": \"OR\", \"Seattle\": \"WA\", \"Oakland\": \"CA\", \"Austin\": \"TX\", \"San Jose\": \"CA\" };\r\n\r\n    \/\/ Paste the public S3 \/ CloudFront CSV URL here.\r\n    const url = \"https:\/\/redfin-public-data.s3.amazonaws.com\/report_htmls\/frothiness_report\/metro-catch-up-by-rate.csv\"\r\n    const DATA_URL = url\r\n    function parseCSV(text) {\r\n      const rows = []; let row = [], field = '', quoted = false;\r\n      for (let i = 0; i < text.length; i++) {\r\n        const ch = text[i], next = text[i + 1];\r\n        if (ch === '\"') { if (quoted) { if (next === '\"') { field += '\"'; i++; } else quoted = !quoted; } else quoted = !quoted; }\r\n        else if (ch === ',') { if (quoted) { field += ch; continue; } row.push(field); field = ''; }\r\n        else if (ch === '\\n' || ch === '\\r') { if (quoted) { field += ch; continue; }\r\n          if (ch === '\\r') { if (next === '\\n') i++; }\r\n          row.push(field); field = '';\r\n          if (row.some(v => v.trim() !== '')) rows.push(row);\r\n          row = [];\r\n        } else field += ch;\r\n      }\r\n      if (field.length || row.length) { row.push(field); if (row.some(v => v.trim() !== '')) rows.push(row); }\r\n      const headers = rows.shift().map(h => h.trim());\r\n      return rows.map(r => Object.fromEntries(headers.map((h, i) => [h, (r[i] ?? '').trim()])));\r\n    }\r\n    function num(v) { const n = Number(String(v ?? '').replace(\/[%,$]\/g, '').trim()); return Number.isFinite(n) ? n : null; }\r\n    function months(v) {\r\n      const raw = String(v ?? '').trim();\r\n      if (!raw) return CAP;\r\n      if (\/10\\+|10 or more|120\\+\/i.test(raw)) return CAP;\r\n      const n = num(raw); return n == null ? CAP : Math.min(n, CAP);\r\n    }\r\n    function metroName(raw) {\r\n      const name = String(raw ?? '').trim();\r\n      if (STATE_BY_METRO[name]) return name;\r\n      const clean = name.replace(\/,\\s*[A-Z]{2}(?:\\s+metro area)?$\/i, '').replace(\/\\s+metro area$\/i, '').trim();\r\n      return STATE_BY_METRO[clean] ? clean : name;\r\n    }\r\n    function rowToMetro(r) {\r\n      const n = metroName(r['Metro']);\r\n      return {\r\n        n, st: STATE_BY_METRO[n] || '', p: num(r['Current Price Growth (%)']) ?? 0, i: num(r['Income Growth (%)']) ?? 0,\r\n        m: [months(r['8.0% Scenario']), months(r['7.5% Scenario']), months(r['7.25% Scenario']), months(r['7.0% Scenario']), months(r['6.5% Scenario']), months(r['6.0% Scenario 2'])]\r\n      };\r\n    }\r\n    const NAT = { n: 'United States', med: 398104, aff: [308703, 321219, 327772, 334531, 348692, 363759], prem: [29.0, 23.9, 21.5, 19.0, 14.2, 9.4] };\r\n    const RATES = [8.0, 7.5, 7.25, 7.0, 6.5, 6.0], K = 1, CAP = 120;\r\n    const state = { metro: -1 };\r\n    const COMPONENT = document.getElementById('housing-map-component');\r\n    const $ = id => COMPONENT.querySelector('#' + id);\r\n    const C = { red: '#DE3341', navy: '#2F3E5A', steel: '#55647F', g600: '#6B6B6B', g500: '#8A8A8A', g300: '#D9D9D9', pink: '#FEE8F4' };\r\n    const rl = r => r.toFixed(2).replace(\/0$\/, '') + '%';\r\n    function dur(m) { if (m === 0) return 'Already there'; if (m >= CAP) return '10+ years'; const y = Math.floor(m \/ 12), mo = m % 12, p = []; if (y) p.push(y + (y === 1 ? ' year' : ' years')); if (mo) p.push(mo + (mo === 1 ? ' month' : ' months')); return p.join(', '); }\r\n    function yr(m) { return 'around ' + (2026 + Math.floor((7 + m) \/ 12)); }\r\n    const money = v => v >= 1e6 ? '$' + (v \/ 1e6).toFixed(2) + 'M' : '$' + Math.round(v \/ 1000) + 'K';\r\n    const pct = v => v.toFixed(1) + '%';\r\n    const full = d => d.n + ', ' + d.st;\r\n    const gapOf = d => ((1 + d.i \/ 100) \/ (1 + d.p \/ 100) - 1) * 100;   \/\/ income lead: (1+income growth)\/(1+price growth)-1, in %\r\n    function gi(d) { const m = d.m[K]; return m < 60 ? 0 : m < CAP ? 1 : (d.m[5] < CAP ? 2 : 3); }\r\n    const GN = ['Within 5 years', '5 to 10 years', '10+ years now, in reach at lower rates', '10+ years, even at 6.0%'];\r\n    const GC = [C.navy, C.steel, C.g300, C.g500], GT = ['#fff', '#fff', '#111', '#111'];\r\n    const avg = (a, f) => a.reduce((s, x) => s + f(x), 0) \/ a.length;\r\n    const G = [0, 1, 2, 3].map(g => D.filter(d => gi(d) === g));\r\n    const gainAvg = a => avg(a, d => d.m[K] - d.m[5]);\r\n    const yrs = m => (m \/ 12).toFixed(1);\r\n    const sgnp = v => (v >= 0 ? '+' : '\u2212') + Math.abs(v).toFixed(1);\r\n    const sgn = v => (v >= 0 ? '+' : '\u2212') + Math.abs(v).toFixed(1) + '%';\r\n\r\n    function scale(d0, d1, r0, r1) { return v => r0 + (v - d0) \/ (d1 - d0) * (r1 - r0); }\r\n\r\n    const HATCH = `<defs><pattern id=\"hatch\" width=\"6\" height=\"6\" patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\"><rect width=\"6\" height=\"6\" fill=\"#F7F7F7\"\/><line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"6\" stroke=\"#D9D9D9\" stroke-width=\"2\"\/><\/pattern><\/defs>`;\r\n    const TMAP = { 8: 1, 7.5: .65, 7.25: .5, 7: .4, 6.5: .2, 6: 0 };\r\n    const lerp = (a, b, t) => Math.round(a + (b - a) * t);\r\n    function rateColor(r) { const t = TMAP[r]; return `rgb(${lerp(129, 47, t)},${lerp(172, 62, t)},${lerp(255, 90, t)})`; }\r\n    const OUT = '#2F3E5A';\r\n    function sdur(m) { if (m === 0) return 'now'; if (m >= CAP) return '10+ yrs'; const y = Math.floor(m \/ 12), mo = m % 12; return (y ? y + 'y ' : '') + (mo ? mo + 'm' : '').trim(); }\r\n\r\n    function dirStyle(d, sel) {\r\n      const g = gapOf(d);\r\n      const base = g >= 3 ? { fill: C.navy, stroke: '#fff', sw: 1 } : g >= 0 ? { fill: C.g500, stroke: '#fff', sw: 1 } : { fill: '#fff', stroke: C.g600, sw: 2 };\r\n      return base;\r\n    }\r\n\r\n\r\n\r\n    const REG = { West: ['CA', 'WA', 'OR', 'NV', 'CO'], South: ['TX', 'FL', 'GA', 'NC', 'TN', 'VA', 'MD'], Midwest: ['IL', 'OH', 'MI', 'MN', 'MO', 'IN', 'WI'], Northeast: ['NY', 'NJ', 'PA', 'MA', 'RI'] };\r\n    const regOf = st => Object.keys(REG).find(r => REG[r].includes(st));\r\n    const CATN = ['Already there', 'Within 5 years', '5 to 10 years', '10+ years'];\r\n    const catOf = m => m === 0 ? 0 : m < 60 ? 1 : m < CAP ? 2 : 3;\r\n    const NAVY = '#2F3E5A';\r\n    \/\/ Consistent wait-time scale across prototypes: light = shorter wait, dark = longer wait.\r\n    function catStyle(c) {\r\n      \/\/outline for the first two categories, filled blues for longer waits.\r\n      return c === 0 ? { fill: '#FFD869', stroke: '#FFD869', sw: 2.5, dr: 0 }\r\n        : c === 1 ? { fill: '#c76d86', stroke: '#c76d86', sw: 2.5, dr: 0 }\r\n          : c === 2 ? { fill: '#de3341', stroke: '#de3341', sw: 1.5, dr: 0 }\r\n            : { fill: '#590213', stroke: '#590213', sw: 1.5, dr: 0 };\r\n    }\r\n    function catLegend() {\r\n      return [0, 1, 2, 3].map(c => {\r\n        const st = catStyle(c);\r\n        return `<span><i style=\"background:${st.fill};\"><\/i>${CATN[c]}<\/span>`;\r\n      }).join('');\r\n    }\r\n    const ORDER = [5, 4, 3, 2, 1, 0];\r\n    function buildPills() {\r\n      COMPONENT.querySelectorAll('.rate-pills').forEach(box => {\r\n        box.innerHTML = ORDER.map(k => `<button class=\"pill\" data-r=\"${k}\" aria-pressed=\"${k === state.rate}\">${rl(RATES[k])}<\/button>`).join('');\r\n        box.addEventListener('click', e => { const b = e.target.closest('button'); if (b) setRate(+b.dataset.r); });\r\n      });\r\n      COMPONENT.querySelectorAll('.catleg').forEach(l => l.innerHTML = catLegend());\r\n    }\r\n    function syncMetroSelect() {\r\n      const d = D[state.metro];\r\n      if (!d) return;\r\n      if ($('msel-button-text')) $('msel-button-text').textContent = full(d);\r\n      if ($('msel-list')) {\r\n        $('msel-list').querySelectorAll('.metro-select-option').forEach(li => {\r\n          const on = +li.dataset.i === state.metro;\r\n          li.classList.toggle('is-selected', on);\r\n          li.setAttribute('aria-selected', on ? 'true' : 'false');\r\n          const check = li.querySelector('.metro-select-check');\r\n          if (check) check.textContent = on ? '\u2713' : '';\r\n        });\r\n      }\r\n    }\r\n\r\n    function buildMetroSelect() {\r\n      const button = $('msel-button'), list = $('msel-list');\r\n      if (!button || !list) return;\r\n      const options = D.map((d, i) => [d, i]).sort((a, b) => a[0].n.localeCompare(b[0].n));\r\n      list.innerHTML = options.map(([d, i]) => `<li class=\"metro-select-option${i === state.metro ? ' is-selected' : ''}\" role=\"option\" aria-selected=\"${i === state.metro}\" data-i=\"${i}\"><span>${full(d)}<\/span><span class=\"metro-select-check\" aria-hidden=\"true\">${i === state.metro ? '\u2713' : ''}<\/span><\/li>`).join('');\r\n      syncMetroSelect();\r\n\r\n      const close = () => { list.hidden = true; button.setAttribute('aria-expanded', 'false'); };\r\n      const open = () => { list.hidden = false; button.setAttribute('aria-expanded', 'true'); };\r\n\r\n      button.addEventListener('click', e => {\r\n        e.stopPropagation();\r\n        button.getAttribute('aria-expanded') === 'true' ? close() : open();\r\n      });\r\n      list.addEventListener('click', e => {\r\n        const li = e.target.closest('.metro-select-option');\r\n        if (!li) return;\r\n        close();\r\n        setMetro(+li.dataset.i);\r\n        button.focus();\r\n      });\r\n      document.addEventListener('click', e => {\r\n        if (!button.parentElement.contains(e.target)) close();\r\n      });\r\n      document.addEventListener('keydown', e => {\r\n        if (e.key === 'Escape') { if (button.getAttribute('aria-expanded') === 'true') { close(); button.focus(); } }\r\n      });\r\n    }\r\n\r\n    function setRate(k) { state.rate = k; COMPONENT.querySelectorAll('.rate-pills button').forEach(b => b.setAttribute('aria-pressed', +b.dataset.r === k)); render(); }\r\n    function bindRateCircleControls(el) {\r\n      if (!el) return;\r\n      el.querySelectorAll('.rate-circle-hit').forEach(node => {\r\n        const choose = () => setRate(+node.dataset.r);\r\n        node.addEventListener('click', choose);\r\n        node.addEventListener('keydown', e => {\r\n          if (e.key === 'Enter' || e.key === ' ') {\r\n            e.preventDefault();\r\n            choose();\r\n          }\r\n        });\r\n      });\r\n    }\r\n    function setMetro(i) {\r\n      state.metro = i;\r\n      if ($('msel')) $('msel').value = i;\r\n      syncMetroSelect();\r\n      render();\r\n    }\r\n    let rt; window.addEventListener('resize', () => { clearTimeout(rt); rt = setTimeout(render, 120); });\r\n    state.rate = 1;\r\n\r\n    function mapView() {\r\n      const el = $('map'), w = Math.max(300, el.clientWidth), sc = w \/ 1055, narrow = w < 520, k = state.rate;\r\n      const r = (narrow ? 6 : 8.5) \/ sc;\r\n      const hitR = (narrow ? 16 : 14) \/ sc;\r\n      const P = D.map((d, i) => ({ d, i, x: MAP.pts[d.n][0], y: MAP.pts[d.n][1] }));\r\n      let s = `<svg viewBox=\"-80 0 1055 610\" aria-hidden=\"true\"><path d=\"${MAP.nation}\" fill=\"#F3EEE7\"\/><path d=\"${MAP.borders}\" fill=\"none\" stroke=\"#fff\" stroke-width=\"1.2\" stroke-linejoin=\"round\"\/>`;\r\n      const labs = narrow ? [] : ['Seattle', 'San Francisco', 'Los Angeles', 'Chicago', 'New York', 'Miami', 'Austin', 'Denver'];\r\n      P.filter(p => p.i !== state.metro).forEach(p => {\r\n        const c = catOf(p.d.m[k]), st = catStyle(c);\r\n        s += `<circle cx=\"${p.x}\" cy=\"${p.y}\" r=\"${r + st.dr \/ sc}\" fill=\"${st.fill}\" fill-opacity=\"1\" \" stroke-width=\"${st.sw \/ sc}\" style=\"mix-blend-mode:multiply;pointer-events:none\"\/>`;\r\n      });\r\n\r\n      \/\/ Draw the selected metro last so both its circle and label stay above overlaps.\r\n      const selected = P.find(p => p.i === state.metro);\r\n      if (selected) {\r\n        const c = catOf(selected.d.m[k]), st = catStyle(c);\r\n        s += `<circle cx=\"${selected.x}\" cy=\"${selected.y}\" r=\"${r + st.dr \/ sc}\" fill=\"${st.fill}\" fill-opacity=\"1\" stroke-width=\"${st.sw \/ sc}\" style=\"mix-blend-mode:multiply;pointer-events:none\"\/>`;\r\n        s += `<circle cx=\"${selected.x}\" cy=\"${selected.y}\" r=\"${r + 5 \/ sc}\" fill=\"none\" stroke=\"#111\" stroke-width=\"${2 \/ sc}\" style=\"pointer-events:none\"\/>`;\r\n      }\r\n\r\n      P.filter(p => p.i !== state.metro).forEach(p => { if (narrow || !labs.includes(p.d.n)) return; const left = p.x < 330; s += `<text x=\"${left ? p.x - r - 5 \/ sc : p.x + r + 5 \/ sc}\" y=\"${p.y + 4 \/ sc}\" text-anchor=\"${left ? 'end' : 'start'}\" paint-order=\"stroke\" stroke=\"#F3EEE7\" stroke-width=\"${3 \/ sc}\" style=\"font-size:${12 \/ sc}px;font-weight:600;fill:#111;pointer-events:none\">${p.d.n}<\/text>`; });\r\n      if (selected) { if (!narrow) {\r\n        const left = selected.x < 330;\r\n        s += `<text x=\"${left ? selected.x - r - 5 \/ sc : selected.x + r + 5 \/ sc}\" y=\"${selected.y + 4 \/ sc}\" text-anchor=\"${left ? 'end' : 'start'}\" paint-order=\"stroke\" stroke=\"#F3EEE7\" stroke-width=\"${3 \/ sc}\" style=\"font-size:${12 \/ sc}px;font-weight:700;fill:#111;pointer-events:none\">${selected.d.n}<\/text>`;\r\n        }\r\n      }\r\n      P.forEach(p => { s += `<circle class=\"mdot-hit\" data-i=\"${p.i}\" cx=\"${p.x}\" cy=\"${p.y}\" r=\"${hitR}\" fill=\"transparent\" stroke=\"none\" style=\"cursor:pointer\"\/>`; });\r\n      el.innerHTML = s + '<\/svg>';\r\n      el.querySelectorAll('.mdot-hit').forEach(c => c.addEventListener('click', () => setMetro(+c.dataset.i)));\r\n      const cnt = Object.keys(REG).map(g => { const a = D.filter(d => regOf(d.st) === g); return [g, a.filter(d => d.m[k] < CAP).length, a.length]; });\r\n      \/\/ $('map-txt').innerHTML = `At ${rl(RATES[k])}, ${cnt.map(([g, n, t], i) => `${i === cnt.length - 1 ? 'and ' : ''}${n} of ${t} in the ${g}`).join(', ')} get back to their 2018 relationship within 10 years.`;\r\n      const d = D[state.metro];\r\n\r\n    }\r\n    function rateLine(d) {\r\n      const el = $('rb'); if (!el) return;\r\n      const w = Math.max(280, el.clientWidth);\r\n      const mobile = window.innerWidth <= 640;\r\n      const sk = state.rate;\r\n      const allCap = d.m.every(m => m >= CAP);\r\n\r\n      if (mobile) {\r\n        const h = 250;\r\n        const labelX = 0;              \/\/ aligns with the card caption above\r\n        const axisX = 104;\r\n        const right = w;               \/\/ extend grid lines to the full chart width\r\n        const ys = scale(0, 132, 24, h - 26);\r\n        let s = `<svg viewBox=\"0 0 ${w} ${h}\" aria-hidden=\"true\">${HATCH}`;\r\n\r\n        \/\/ Vertical time axis: labels align to the caption's left edge and rules run to the end.\r\n        [[0,'Today'],[24,'2 yrs'],[60,'5 yrs'],[120,'10 yrs']].forEach(([v,tt]) => {\r\n          s += `<text x=\"${labelX}\" y=\"${ys(v)+4}\" text-anchor=\"start\">${tt}<\/text>`;\r\n          s += `<line x1=\"${axisX}\" x2=\"${right}\" y1=\"${ys(v)}\" y2=\"${ys(v)}\" stroke=\"#D9D9D9\"\/>`;\r\n        });\r\n        s += `<rect x=\"${axisX}\" y=\"${ys(120)}\" width=\"${right-axisX}\" height=\"${ys(132)-ys(120)}\" fill=\"url(#hatch)\"\/>`;\r\n\r\n        if (allCap) {\r\n          const st = catStyle(3);\r\n          const cy = ys(126), cx = axisX + 20;\r\n          s += `<circle cx=\"${cx}\" cy=\"${cy}\" r=\"8\" fill=\"${st.fill}\" stroke=\"${st.stroke}\" stroke-width=\"2\"\/>`;\r\n          s += `<circle cx=\"${cx}\" cy=\"${cy}\" r=\"12\" fill=\"none\" stroke=\"#111\" stroke-width=\"2\"\/>`;\r\n          s += `<text x=\"${cx + 18}\" y=\"${cy + 4}\" text-anchor=\"start\" style=\"font-size:13px;font-weight:700;fill:#111\">10+ years at every rate<\/text>`;\r\n          el.innerHTML = s + '<\/svg>';\r\n          return;\r\n        }\r\n\r\n        const cm = v => v >= CAP ? 126 : v;\r\n        const groups = new Map();\r\n        [0,1,2,3,4,5].forEach(k => {\r\n          const key = cm(d.m[k]);\r\n          if (!groups.has(key)) groups.set(key, []);\r\n          groups.get(key).push(k);\r\n        });\r\n\r\n        \/\/ Keep the time position exact. If rates share the same wait, separate them horizontally.\r\n        const pos = {};\r\n        groups.forEach(keys => {\r\n          keys.sort((a,b) => RATES[a]-RATES[b]);\r\n          const spacing = 16;\r\n          keys.forEach((k,j) => {\r\n            pos[k] = {\r\n              x: axisX + 54 + (j-(keys.length-1)\/2)*spacing,\r\n              y: ys(cm(d.m[k]))\r\n            };\r\n          });\r\n        });\r\n\r\n        \/\/ Same connector treatment as desktop: light gray, 2px, through every scenario circle.\r\n        const connectorOrder = [5,4,3,2,1,0];\r\n        s += `<polyline points=\"${connectorOrder.map(k => `${pos[k].x},${pos[k].y}`).join(' ')}\" fill=\"none\" stroke=\"#D9D9D9\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/>`;\r\n\r\n        [0,1,2,3,4,5].forEach(k => {\r\n          const st = catStyle(catOf(d.m[k]));\r\n          s += `<circle cx=\"${pos[k].x}\" cy=\"${pos[k].y}\" r=\"6\" fill=\"${st.fill}\" stroke=\"${st.stroke}\" stroke-width=\"2\" style=\"pointer-events:none\"\/>`;\r\n        });\r\n        s += `<circle cx=\"${pos[sk].x}\" cy=\"${pos[sk].y}\" r=\"10\" fill=\"none\" stroke=\"#111\" stroke-width=\"2\" style=\"pointer-events:none\"\/>`;\r\n        [0,1,2,3,4,5].forEach(k => {\r\n          s += `<circle class=\"rate-circle-hit\" data-r=\"${k}\" cx=\"${pos[k].x}\" cy=\"${pos[k].y}\" r=\"14\" fill=\"transparent\" stroke=\"none\" role=\"button\" tabindex=\"0\" aria-label=\"Show ${rl(RATES[k])} mortgage rate\" style=\"cursor:pointer\"\/>`;\r\n        });\r\n\r\n        \/\/ Label only 6%, the selected rate, and 8%. Put rate + duration on the same horizontal row as the circle.\r\n        const labelKs = [...new Set([5, sk, 0])];\r\n        labelKs.forEach(k => {\r\n          const p = pos[k];\r\n          const rateX = p.x + 12;\r\n          s += `<text x=\"${rateX}\" y=\"${p.y + 4}\" text-anchor=\"start\" style=\"font-size:13px;font-weight:${k===sk?700:600};fill:#111\">${rl(RATES[k])}<\/text>`;\r\n          s += `<text class=\"anno-l\" x=\"${rateX + 42}\" y=\"${p.y + 4}\" text-anchor=\"start\">${sdur(d.m[k])}<\/text>`;\r\n        });\r\n\r\n        el.innerHTML = s + '<\/svg>';\r\n        bindRateCircleControls(el);\r\n        return;\r\n      }\r\n\r\n      const narrow = w < 440;\r\n      const top = 52, h = top + 68, xs = scale(0, 132, 16, w - 16), baseY = top + 16;\r\n      let s = `<svg viewBox=\"0 0 ${w} ${h}\" aria-hidden=\"true\">${HATCH}`;\r\n      s += `<rect x=\"${xs(120)}\" y=\"${top - 8}\" width=\"${xs(132) - xs(120)}\" height=\"${h - top + 4}\" fill=\"url(#hatch)\"\/><g class=\"axis\">`;\r\n      [[0, narrow ? '' : 'Today'], [24, '2 yrs'], [60, '5 yrs'], [120, '10 yrs']].forEach(([v, tt]) => { s += `<line x1=\"${xs(v)}\" x2=\"${xs(v)}\" y1=\"${top - 8}\" y2=\"${h - 4}\" stroke=\"#D9D9D9\"\/><text x=\"${v === 120 ? xs(v) - 3 : xs(v)}\" y=\"${top - 24}\" text-anchor=\"${v === 0 ? 'start' : v === 120 ? 'end' : 'middle'}\">${tt}<\/text>`; });\r\n      s += '<\/g>';\r\n\r\n      const cm = v => v >= CAP ? 126 : v, px = k => xs(cm(d.m[k]));\r\n      const lo = Math.min(...d.m.map(cm)), hi = Math.max(...d.m.map(cm));\r\n\r\n      if (allCap) {\r\n        const st = catStyle(3), x = xs(126);\r\n        s += `<circle cx=\"${x}\" cy=\"${baseY}\" r=\"7\" fill=\"${st.fill}\" stroke=\"${st.stroke}\" stroke-width=\"2\"\/>`;\r\n        s += `<circle cx=\"${x}\" cy=\"${baseY}\" r=\"11\" fill=\"none\" stroke=\"#111\" stroke-width=\"2\"\/>`;\r\n        s += `<text x=\"${x - 4}\" y=\"${baseY - 16}\" text-anchor=\"end\" style=\"font-size:13px;font-weight:700;fill:#111\">10+ years at every rate<\/text>`;\r\n        el.innerHTML = s + '<\/svg>';\r\n        return;\r\n      }\r\n\r\n      if (hi > lo) s += `<line x1=\"${xs(lo)}\" x2=\"${xs(hi)}\" y1=\"${baseY}\" y2=\"${baseY}\" stroke=\"#D9D9D9\" stroke-width=\"2\" stroke-linecap=\"round\"\/>`;\r\n\r\n      const groups = new Map();\r\n      [0,1,2,3,4,5].forEach(k => { const key = cm(d.m[k]); if (!groups.has(key)) groups.set(key, []); groups.get(key).push(k); });\r\n      const py = {};\r\n      groups.forEach(keys => {\r\n        keys.sort((a,b) => RATES[b] - RATES[a]);\r\n        const spacing = 8;\r\n        keys.forEach((k, j) => { py[k] = baseY + (j - (keys.length - 1) \/ 2) * spacing; });\r\n      });\r\n\r\n      const dotR = 6;\r\n      [0,1,2,3,4,5].forEach(k => {\r\n        const st = catStyle(catOf(d.m[k]));\r\n        s += `<circle cx=\"${px(k)}\" cy=\"${py[k]}\" r=\"${dotR}\" fill=\"${st.fill}\" stroke=\"${st.stroke}\" stroke-width=\"2\" style=\"pointer-events:none\"\/>`;\r\n      });\r\n      s += `<circle cx=\"${px(sk)}\" cy=\"${py[sk]}\" r=\"${dotR + 4}\" fill=\"none\" stroke=\"#111\" stroke-width=\"2\" style=\"pointer-events:none\"\/>`;\r\n      [0,1,2,3,4,5].forEach(k => {\r\n        s += `<circle class=\"rate-circle-hit\" data-r=\"${k}\" cx=\"${px(k)}\" cy=\"${py[k]}\" r=\"14\" fill=\"transparent\" stroke=\"none\" role=\"button\" tabindex=\"0\" aria-label=\"Show ${rl(RATES[k])} mortgage rate\" style=\"cursor:pointer\"\/>`;\r\n      });\r\n\r\n      const labelKs = [...new Set([5, sk, 0])];\r\n      const labelGroups = new Map();\r\n      labelKs.forEach(k => { const key = cm(d.m[k]); if (!labelGroups.has(key)) labelGroups.set(key, []); labelGroups.get(key).push(k); });\r\n      [...labelGroups.entries()].sort((a,b) => a[0]-b[0]).forEach(([key, keys]) => {\r\n        const x = xs(key), hasSelected = keys.includes(sk);\r\n        const label = keys.map(k => rl(RATES[k])).join(' \u00b7 ');\r\n        const yTop = Math.min(...keys.map(k => py[k])) - 13;\r\n        const yBottom = Math.max(...keys.map(k => py[k])) + 22;\r\n        s += `<text x=\"${x}\" y=\"${yTop}\" text-anchor=\"middle\" style=\"font-size:12px;font-weight:${hasSelected ? 700 : 600};fill:#111\">${label}<\/text>`;\r\n        s += `<text class=\"anno-l\" x=\"${x}\" y=\"${yBottom}\" text-anchor=\"middle\">${sdur(d.m[hasSelected ? sk : keys[0]])}<\/text>`;\r\n      });\r\n\r\n      el.innerHTML = s + '<\/svg>';\r\n      bindRateCircleControls(el);\r\n    }\r\n\r\n\r\n    function card() {\r\n      const d = D[state.metro], k = state.rate, m = d.m[k];\r\n      const result = m === 0\r\n        ? `Already at normal at ${rl(RATES[k])}`\r\n        : m >= CAP\r\n          ? `10+ years to return to normal at ${rl(RATES[k])}`\r\n          : `Return to normal ${yr(m)} at ${rl(RATES[k])}`;\r\n      $('cardbody').innerHTML = `\r\n    <div class=\"big\">${result}<\/div>\r\n    <div class=\"cap\" style=\"margin:8px 0 0\">Wait to get back to normal, from 6.0% to 8.0%<\/div>\r\n    <div id=\"rb\" role=\"img\" aria-label=\"Wait at each mortgage rate: ${RATES.map((r, k) => rl(r) + ' ' + (d.m[k] >= CAP ? '10 or more years' : d.m[k] + ' months')).join(', ')}.\"><\/div>`;\r\n      rateLine(d);\r\n    }\r\n\r\n    function render() { mapView(); card(); }\r\n\r\n    async function boot() {\r\n      try {\r\n        const res = await fetch(DATA_URL, { cache: 'no-store' });\r\n        if (!res.ok) throw new Error(`Data request failed: ${res.status} ${res.statusText}`);\r\n        const rows = parseCSV(await res.text());\r\n        D = rows.map(rowToMetro).filter(d => MAP.pts[d.n]);\r\n        if (!D.length) throw new Error('No CSV metros matched the coordinate lookup in this file.');\r\n\r\n        state.metro = Math.max(0, D.findIndex(d => d.n === 'Los Angeles'));\r\n        $('msel').innerHTML = D.map((d, i) => [d, i]).sort((a, b) => a[0].n.localeCompare(b[0].n)).map(([d, i]) => `<option value=\"${i}\">${full(d)}<\/option>`).join('');\r\n        $('msel').value = state.metro;\r\n        buildMetroSelect();\r\n        buildPills();\r\n        render();\r\n      } catch (err) {\r\n        console.error(err);\r\n        $('map').innerHTML = `<div style=\"padding:24px;color:#666;font:14px\/1.5 Arial,sans-serif\"><b>Unable to load metro data.<\/b><br>${err.message}<\/div>`;\r\n      }\r\n    }\r\n    boot();\r\n    })();\r\n<\/script><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">After San Jose, Austin, TX is the closest to returning to normal housing costs. With 7.5% mortgage rates, costs could return to normal by early 2028. That\u2019s because home prices are down 2.9% year over year in Austin, while we project wages to post annual growth of 4.9%. Prices have fallen in Austin because of slow homebuying demand combined with lots of supply, which is largely the result of pandemic-era overbuilding.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">Next comes another Bay Area metro, Oakland, where housing costs could return to normal by spring 2028 with 7.5% rates. Two other West Coast metros, Seattle and Portland, OR, round out the top five metro areas where costs could return to normal soonest.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">For house hunters in San Jose, Austin and the rest of the places in the table below, falling home prices\u2013or even weak price growth\u2013means a big rate drop may not be necessary for housing costs to return to normal. And in places where we expect particularly strong income increases, a big rate drop may not be necessary, either.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">In San Jose, Austin and Oakland, housing costs could return to normal <\/span><i><span style=\"font-weight: 400;\">now <\/span><\/i><span style=\"font-weight: 400;\">if mortgage rates were to drop to 6%.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">Note that Oakland and Seattle\u2013and Portland, to a lesser extent\u2013all fall into a similar boat as San Jose: Housing costs may be relatively close to \u201cnormal,\u201d but that doesn\u2019t mean they\u2019re affordable for the average household. Even though locals tend to earn more money than the average American, homes in those places are pricey; they\u2019re <\/span><a href=\"https:\/\/www.redfin.com\/news\/affordability-homebuying-2026\/\"><span style=\"font-weight: 400;\">out of reach<\/span><\/a><span style=\"font-weight: 400;\"> for a lot of residents. In these metros, homeownership tends to be concentrated among relatively wealthy households.<\/span><\/p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td colspan=\"9\">\r\n<p><b>The 10 Housing Markets That Could Return to Normal Soonest<\/b><\/p>\r\n<p><i><span style=\"font-weight: 400;\">Defined by August 2018 levels, based on annual home sale price growth and mortgage rate scenarios.\u00a0<\/span><\/i><\/p>\r\n<p><i><span style=\"font-weight: 400;\">Assuming current local price growth, and projected income growth based on historical trends.\u00a0<\/span><\/i><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><b>U.S. metro area<\/b><\/p>\r\n<\/td>\r\n<td>\r\n<p><b>Current Price Growth, YoY Change<\/b><\/p>\r\n<\/td>\r\n<td>\r\n<p><b>Projected Annual Income Growth<\/b><\/p>\r\n<\/td>\r\n<td>\r\n<p><b>8%\u00a0<\/b><\/p>\r\n<\/td>\r\n<td>\r\n<p><b>7.5%\u00a0<\/b><\/p>\r\n<\/td>\r\n<td>\r\n<p><b>7.25%\u00a0<\/b><\/p>\r\n<\/td>\r\n<td>\r\n<p><b>7%\u00a0<\/b><\/p>\r\n<\/td>\r\n<td>\r\n<p><b>6.5%\u00a0<\/b><\/p>\r\n<\/td>\r\n<td>\r\n<p><b>6%\u00a0<\/b><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">San Jose,\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">CA<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">-3.2%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">6.5%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">March\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2028<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">October 2027<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">July 2027<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">April 2027<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">November 2026<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><i><span style=\"font-weight: 400;\">Now<\/span><\/i><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">Austin,\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">TX<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">-2.9%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">4.9%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">August 2028<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">February 2028<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">December 2027<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">September 2027<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">March 2027<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><i><span style=\"font-weight: 400;\">Now<\/span><\/i><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">Oakland,\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">CA<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">-0.3%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">6.5%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">November 2028<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">April\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2028<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">December 2027<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">September 2027<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">January 2027<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><i><span style=\"font-weight: 400;\">Now<\/span><\/i><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">Seattle,\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">WA<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">-2.9%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">5.8%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">December 2029<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">June\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2029<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">April\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2029<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">January 2029<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">July\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2028<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">January 2028<\/span><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">Portland,\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">OR<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">-0.1%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">5.6%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">November 2030<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">February 2030<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">October\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2029<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">June\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2029<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">September 2028<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">December 2027<\/span><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">San Antonio, TX<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">-0.8%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">3.2%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">January 2032<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">January\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2031<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">July\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2030<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">January 2030<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">January 2029<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">January 2028<\/span><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">Sacramento, CA<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">0%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">4.9%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">February 2032<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">April\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2031<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">November 2030<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">June\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2030<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">July\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2029<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">September 2028<\/span><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">Denver,\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">CO<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">0.7%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">4.9%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">June\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2033<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">June\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2032<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">November 2031<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">May\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2031<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">April\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2030<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">March 2029<\/span><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">Los Angeles, CA<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">0.7%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">4.9%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">June\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2033<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">June\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2032<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">December 2031<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">June\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2031<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">June\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2030<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">May\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2029<\/span><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">Fort Worth, TX<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">-0.9%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">3.5%<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">December 2033<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">February 2033<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">September 2033<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">April\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2032<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">May\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2031<\/span><\/p>\r\n<\/td>\r\n<td>\r\n<p><span style=\"font-weight: 400;\">July\u00a0<\/span><\/p>\r\n<p><span style=\"font-weight: 400;\">2030<\/span><\/p>\r\n<\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\"><\/figure>\r\n\r\n\r\n\r\n<h2 id=\"h-it-could-take-10-years-for-housing-costs-to-get-back-to-normal-in-the-northeast-and-midwest\" class=\"wp-block-heading\"><b>It Could Take 10+ Years For Housing Costs to Get Back to \u201cNormal\u201d in the Northeast and Midwest<\/b><\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">&nbsp;<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">In about half of the metros we analyzed, it could take at least a decade for housing costs to normalize. Many of them are in the Northeast or Midwest, including Boston, the New York City area, Chicago and Milwaukee\u2014and many are places where home prices are growing faster than the national average.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">In Chicago, for instance, home prices are up 5.5% year over year, while projected income growth is 3.9%. In Nassau County, NY (Long Island), home prices are up 5.3%, while projected income growth is 3.6%. It\u2019ll take at least 10 years for housing costs to get back to August 2018 levels in both metros under all the mortgage-rate scenarios.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">Price growth is strong in most of these places because they\u2019re generally more competitive markets than the West Coast or the Sun Belt. Nassau County is the <\/span><a href=\"https:\/\/www.redfin.com\/news\/buyers-vs-sellers-august-2026\/\"><span style=\"font-weight: 400;\">strongest seller\u2019s market in the nation<\/span><\/a><span style=\"font-weight: 400;\">, for instance, and Chicago is hotter than most other U.S. metros.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">\u201cIt may seem counterintuitive that housing costs could return to normal sooner in the country\u2019s most expensive markets than in a place like Chicago or Philly, but it comes down to the direction of home prices and incomes,\u201d Khan said. \u201cIn parts of the West, home prices are falling while we expect incomes to keep rising, gradually bringing the markets back to a baseline. But in many Midwest and East Coast markets, home prices are still climbing faster than incomes\u2013so even if rates were to drop meaningfully, a buyer\u2019s monthly payment wouldn\u2019t return to something that feels normal anytime soon.\u201d<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">Here are all the metros it could take at least a decade for housing costs to normalize, if mortgage rates stay between 6% and 8% and price growth continues at its current pace:\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li><span style=\"font-weight: 400;\">Anaheim, CA<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Baltimore, MD<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Chicago, IL<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Cincinnati, OH<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Cleveland, OH<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Columbus, OH<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Detroit, MI<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Fort Lauderdale, FL<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Indianapolis, IN<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Jacksonville, FL\u00a0<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Kansas City, MO<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Milwaukee, WI<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Montgomery County, PA<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Nassau County, NY\u00a0<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">New Brunswick, NJ<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Newark, NJ<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">New York, NY<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Philadelphia, PA<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Providence, RI<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">St. Louis, MO<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Tampa, FL<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Virginia Beach, VA<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Warren, MI\u00a0<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">West Palm Beach, FL<\/span><\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<h2 id=\"h-san-francisco-housing-costs-would-be-normal-at-7-25-rates-but-not-at-7-5\" class=\"wp-block-heading\"><b>San Francisco Housing Costs Would Be \u201cNormal\u201d at 7.25% Rates\u2014But Not at 7.5%<\/b><\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">&nbsp;<\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">San Francisco is a unique case. As recently as the third week of September, when mortgage rates were sitting around <\/span><a href=\"https:\/\/www.mortgagenewsdaily.com\/mortgage-rates\/30-year-fixed\"><span style=\"font-weight: 400;\">7.25%<\/span><\/a><span style=\"font-weight: 400;\">, local housing costs had just barely returned to normal. But that balance is fragile: Even if rates were holding steady in the low-7% range, San Francisoc\u2019s home prices are climbing fast\u20139.7% year over year, thanks to <\/span><a href=\"https:\/\/www.redfin.com\/news\/two-tech-cities-housing-san-francisco-seattle\/\"><span style=\"font-weight: 400;\">AI-fueled wealth<\/span><\/a><span style=\"font-weight: 400;\">\u2013which alone would be enough to push costs out of normal range fairly soon.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\"><span style=\"font-weight: 400;\">And when rates hit 7.5%, costs soared well past normal. That\u2019s despite local incomes that are projected to stay strong, growing 7.7% on the strength of San Francisco\u2019s tech-driven economy. With 7.5% or 8% rates, it would take at least a decade for San Francisco\u2019s costs to return to August 2018 levels, largely because home prices are growing so fast. Once rates hit a certain threshold, housing costs stay unaffordable for a long time\u2013as long as home prices don\u2019t lose steam or start falling.\u00a0<\/span><\/p>\r\n\r\n\r\n\r\n<figure class=\"wp-block-table\">\r\n<table class=\"has-fixed-layout\">\r\n<tbody>\r\n<tr>\r\n<td colspan=\"2\"><b>San Francisco Metro Area: When Housing Costs Could Return to Normal<\/b>\r\n<p>&nbsp;<\/p>\r\n<p><i><span style=\"font-weight: 400;\">Defined by August 2018 levels, based on annual home sale price growth and mortgage rate scenarios.\u00a0<\/span><\/i><\/p>\r\n<p><i><span style=\"font-weight: 400;\">Assuming current local price growth (9.7% YoY), and projected income growth based on historical trends (7.7%).\u00a0<\/span><\/i><\/p>\r\n<\/td>\r\n<\/tr>\r\n<tr>\r\n<td><b>8%<\/b><\/td>\r\n<td><span style=\"font-weight: 400;\">In 10+ Years<\/span><\/td>\r\n<\/tr>\r\n<tr>\r\n<td><b>7.5%<\/b><\/td>\r\n<td><span style=\"font-weight: 400;\">In 10+ Years\u00a0<\/span><\/td>\r\n<\/tr>\r\n<tr>\r\n<td><b>7.25%<\/b><\/td>\r\n<td><i><span style=\"font-weight: 400;\">Now<\/span><\/i><\/td>\r\n<\/tr>\r\n<tr>\r\n<td><b>7%<\/b><\/td>\r\n<td><i><span style=\"font-weight: 400;\">Now<\/span><\/i><\/td>\r\n<\/tr>\r\n<tr>\r\n<td><b>6.5%<\/b><\/td>\r\n<td><i><span style=\"font-weight: 400;\">Now<\/span><\/i><\/td>\r\n<\/tr>\r\n<tr>\r\n<td><b>6%<\/b><\/td>\r\n<td><i><span style=\"font-weight: 400;\">Now<\/span><\/i><\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/figure>\r\n\r\n\r\n\r\n<h2 id=\"h-methodology\" class=\"wp-block-heading\"><b>Methodology<\/b><\/h2>\r\n\r\n\r\n\r\n<p class=\"wp-block-paragraph\">&nbsp;<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li><span style=\"font-weight: 400;\">This analysis uses the mortgage payment-to-income ratio as a measure of housing costs.\u00a0<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">This is the ratio of the monthly housing payment on a 30-year mortgage (mortgage, property tax, insurance) to the median household income for a given region.\u00a0<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">We assume a 20% down payment when measuring the mortgage payment.\u00a0<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Please note that the property tax rate is the median rate observed in the baseline period in the relevant region. The insurance rate is fixed at 0.5% annually.\u00a0<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Median home prices used for the calculation are seasonally adjusted.<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">We compare costs using five 30-year mortgage rates, all of which align generally with economists\u2019 forecasts for the next 3-5 years.\u00a0<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">Household income is measured growing at a compound annual growth rate (CAGR) based on historical Census data (2015\u20132019).<\/span><\/li>\r\n\r\n\r\n\r\n<li><span style=\"font-weight: 400;\">We examined the 50 most populous U.S. metro areas, and included the 46 with sufficient data in this report.<\/span><\/li>\r\n<\/ul>\r\n","protected":false},"excerpt":{"rendered":"<p>Housing costs could hypothetically return to \u201cnormal\u201d within the next five years if mortgage rates drop to 6% and home-price growth holds steady around 2.1%. Alternatively, housing costs could return to normal in just a slightly longer timeline\u2013within about six years\u2013if mortgage rates stay where they are today, about 7.5%, and home-price growth flattens. For [&hellip;]<\/p>\n","protected":false},"author":13153,"featured_media":87579,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"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":"default","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"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":""},"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":""},"categories":[8],"tags":[20],"dashboard":[],"coauthors":[124,710,878],"class_list":["post-87577","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-housing-market-news","tag-housing-affordability"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v24.7 (Yoast SEO v28.5) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take.<\/title>\n<meta name=\"description\" content=\"U.S. housing costs could return to \u201cnormal\u201d within the next five or six years if mortgage rates fall and\/or price growth flattens. But the timeline varies widely from metro to metro.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take.\" \/>\n<meta property=\"og:description\" content=\"U.S. housing costs could return to \u201cnormal\u201d within the next five or six years if mortgage rates fall and\/or price growth flattens. But the timeline varies widely from metro to metro.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/\" \/>\n<meta property=\"og:site_name\" content=\"Redfin Real Estate News\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/redfin\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-08T12:00:02+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-08T18:01:02+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-07-at-10.20.13-AM.png\" \/>\n\t<meta property=\"og:image:width\" content=\"887\" \/>\n\t<meta property=\"og:image:height\" content=\"493\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Dana Anderson, Asad Khan, Szu Yu Chen\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@redfin\" \/>\n<meta name=\"twitter:site\" content=\"@redfin\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Dana Anderson, Asad Khan, Szu Yu Chen\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/\"},\"author\":{\"name\":\"Dana Anderson\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#\\\/schema\\\/person\\\/309385292bf91585d2e0a130b0a2566b\"},\"headline\":\"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take.\",\"datePublished\":\"2026-10-08T12:00:02+00:00\",\"dateModified\":\"2026-10-08T18:01:02+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/\"},\"wordCount\":1951,\"publisher\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.redfin.com/news\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/Screenshot-2026-10-07-at-10.20.13-AM.png\",\"keywords\":[\"Housing Affordability\"],\"articleSection\":[\"Housing Market News\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2026\",\"copyrightHolder\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/\",\"url\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/\",\"name\":\"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take.\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.redfin.com/news\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/Screenshot-2026-10-07-at-10.20.13-AM.png\",\"datePublished\":\"2026-10-08T12:00:02+00:00\",\"dateModified\":\"2026-10-08T18:01:02+00:00\",\"description\":\"U.S. housing costs could return to \u201cnormal\u201d within the next five or six years if mortgage rates fall and\\\/or price growth flattens. But the timeline varies widely from metro to metro.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.redfin.com/news\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/Screenshot-2026-10-07-at-10.20.13-AM.png\",\"contentUrl\":\"https:\\\/\\\/www.redfin.com/news\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/Screenshot-2026-10-07-at-10.20.13-AM.png\",\"width\":887,\"height\":493},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/return-to-normal-housing-costs\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.redfin.com/news\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take.\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#website\",\"url\":\"https:\\\/\\\/www.redfin.com/news\\\/\",\"name\":\"Redfin Real Estate News\",\"description\":\"The latest real estate news and research from technology-powered residential real estate company, Redfin.\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.redfin.com/news\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#organization\",\"name\":\"Redfin\",\"url\":\"https:\\\/\\\/www.redfin.com/news\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.redfin.com/news\\\/wp-content\\\/uploads\\\/2020\\\/10\\\/Redfin-News-Logo.png\",\"contentUrl\":\"https:\\\/\\\/www.redfin.com/news\\\/wp-content\\\/uploads\\\/2020\\\/10\\\/Redfin-News-Logo.png\",\"width\":1100,\"height\":235,\"caption\":\"Redfin\"},\"image\":{\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/redfin\",\"https:\\\/\\\/x.com\\\/redfin\",\"https:\\\/\\\/www.instagram.com\\\/redfinrealestate\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/redfin\",\"https:\\\/\\\/www.pinterest.com\\\/redfin\\\/\",\"https:\\\/\\\/en.wikipedia.org\\\/wiki\\\/Redfin\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/#\\\/schema\\\/person\\\/309385292bf91585d2e0a130b0a2566b\",\"name\":\"Dana Anderson\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.redfin.com/news\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/Dana_Anderson_722_RT-150x150.jpg6a0821cd173d24b4c47bf9d2c09ca097\",\"url\":\"https:\\\/\\\/www.redfin.com/news\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/Dana_Anderson_722_RT-150x150.jpg\",\"contentUrl\":\"https:\\\/\\\/www.redfin.com/news\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/Dana_Anderson_722_RT-150x150.jpg\",\"caption\":\"Dana Anderson\"},\"description\":\"As a data journalist at Redfin, Dana Anderson writes about the numbers behind real estate trends. Redfin is a full-service real estate brokerage that uses modern technology to make clients smarter and faster. For more information about working with a Redfin real estate agent to buy or sell a home, visit our Why Redfin page.\",\"url\":\"https:\\\/\\\/www.redfin.com/news\\\/author\\\/dana-andersonredfin-com\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take.","description":"U.S. housing costs could return to \u201cnormal\u201d within the next five or six years if mortgage rates fall and\/or price growth flattens. But the timeline varies widely from metro to metro.","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:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/","og_locale":"en_US","og_type":"article","og_title":"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take.","og_description":"U.S. housing costs could return to \u201cnormal\u201d within the next five or six years if mortgage rates fall and\/or price growth flattens. But the timeline varies widely from metro to metro.","og_url":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/","og_site_name":"Redfin Real Estate News","article_publisher":"https:\/\/www.facebook.com\/redfin","article_published_time":"2026-10-08T12:00:02+00:00","article_modified_time":"2026-10-08T18:01:02+00:00","og_image":[{"width":887,"height":493,"url":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-07-at-10.20.13-AM.png","type":"image\/png"}],"author":"Dana Anderson, Asad Khan, Szu Yu Chen","twitter_card":"summary_large_image","twitter_creator":"@redfin","twitter_site":"@redfin","twitter_misc":{"Written by":"Dana Anderson, Asad Khan, Szu Yu Chen","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/#article","isPartOf":{"@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/"},"author":{"name":"Dana Anderson","@id":"https:\/\/www.redfin.com\/news\/#\/schema\/person\/309385292bf91585d2e0a130b0a2566b"},"headline":"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take.","datePublished":"2026-10-08T12:00:02+00:00","dateModified":"2026-10-08T18:01:02+00:00","mainEntityOfPage":{"@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/"},"wordCount":1951,"publisher":{"@id":"https:\/\/www.redfin.com\/news\/#organization"},"image":{"@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/#primaryimage"},"thumbnailUrl":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-07-at-10.20.13-AM.png","keywords":["Housing Affordability"],"articleSection":["Housing Market News"],"inLanguage":"en-US","copyrightYear":"2026","copyrightHolder":{"@id":"https:\/\/www.redfin.com\/news\/#organization"}},{"@type":"WebPage","@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/","url":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/","name":"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take.","isPartOf":{"@id":"https:\/\/www.redfin.com\/news\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/#primaryimage"},"image":{"@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/#primaryimage"},"thumbnailUrl":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-07-at-10.20.13-AM.png","datePublished":"2026-10-08T12:00:02+00:00","dateModified":"2026-10-08T18:01:02+00:00","description":"U.S. housing costs could return to \u201cnormal\u201d within the next five or six years if mortgage rates fall and\/or price growth flattens. But the timeline varies widely from metro to metro.","breadcrumb":{"@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/#primaryimage","url":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-07-at-10.20.13-AM.png","contentUrl":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-07-at-10.20.13-AM.png","width":887,"height":493},{"@type":"BreadcrumbList","@id":"https:\/\/www.redfin.com\/news\/return-to-normal-housing-costs\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.redfin.com\/news\/"},{"@type":"ListItem","position":2,"name":"U.S. Housing Costs Could Return to \u201cNormal\u201d Within 5 Years. Here\u2019s What It Would Take."}]},{"@type":"WebSite","@id":"https:\/\/www.redfin.com\/news\/#website","url":"https:\/\/www.redfin.com\/news\/","name":"Redfin Real Estate News","description":"The latest real estate news and research from technology-powered residential real estate company, Redfin.","publisher":{"@id":"https:\/\/www.redfin.com\/news\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.redfin.com\/news\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.redfin.com\/news\/#organization","name":"Redfin","url":"https:\/\/www.redfin.com\/news\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.redfin.com\/news\/#\/schema\/logo\/image\/","url":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2020\/10\/Redfin-News-Logo.png","contentUrl":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2020\/10\/Redfin-News-Logo.png","width":1100,"height":235,"caption":"Redfin"},"image":{"@id":"https:\/\/www.redfin.com\/news\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/redfin","https:\/\/x.com\/redfin","https:\/\/www.instagram.com\/redfinrealestate\/","https:\/\/www.linkedin.com\/company\/redfin","https:\/\/www.pinterest.com\/redfin\/","https:\/\/en.wikipedia.org\/wiki\/Redfin"]},{"@type":"Person","@id":"https:\/\/www.redfin.com\/news\/#\/schema\/person\/309385292bf91585d2e0a130b0a2566b","name":"Dana Anderson","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2023\/10\/Dana_Anderson_722_RT-150x150.jpg6a0821cd173d24b4c47bf9d2c09ca097","url":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2023\/10\/Dana_Anderson_722_RT-150x150.jpg","contentUrl":"https:\/\/www.redfin.com\/news\/wp-content\/uploads\/2023\/10\/Dana_Anderson_722_RT-150x150.jpg","caption":"Dana Anderson"},"description":"As a data journalist at Redfin, Dana Anderson writes about the numbers behind real estate trends. Redfin is a full-service real estate brokerage that uses modern technology to make clients smarter and faster. For more information about working with a Redfin real estate agent to buy or sell a home, visit our Why Redfin page.","url":"https:\/\/www.redfin.com\/news\/author\/dana-andersonredfin-com\/"}]}},"_links":{"self":[{"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/posts\/87577","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/users\/13153"}],"replies":[{"embeddable":true,"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/comments?post=87577"}],"version-history":[{"count":10,"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/posts\/87577\/revisions"}],"predecessor-version":[{"id":87600,"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/posts\/87577\/revisions\/87600"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/media\/87579"}],"wp:attachment":[{"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/media?parent=87577"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/categories?post=87577"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/tags?post=87577"},{"taxonomy":"dashboard","embeddable":true,"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/dashboard?post=87577"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.redfin.com\/news\/wp-json\/wp\/v2\/coauthors?post=87577"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}