{"version":3,"sources":["world-card.mjs"],"sourcesContent":["// THE WORLD CARD'S FACTS (the owner's \"World Card Ideas\" picks, confirmed Oct 8): what the selection card says about a\n// world, worked out from plain data so tests/world-card-ideas.test.mjs can drive it. app.js gathers the data (the focus\n// info, the claim, the roster, the player's own record) in navBodyModel, twice a second at most; nav-hud.js draws it.\n//\n//   chips      air, temperature, water, hazards: [text, tone, tip]. The tone colours the chip (good/poor/bad for what the\n//              suit makes of it, water/gas for what a thermos or a canister can take); the tip says why, in the hazard\n//              engine's own numbers (chiptips: hover or tap a chip).\n//   size       diameter, length of day, tidally locked, read off the info panel's own lines.\n//   people     how many players are on the world, friends named first; a friend there or in its system to join.\n//   history    what you built on a world, from your own record of it.\n//   biomes     the bar under the title and the stacked Biomes panel: each biome's colour and share.\n//   landing    the nearest dry column to the one under the crosshair (Land puts you on dry ground).\nimport { HAZARD } from './hazard-engine.js';\n\n// ONE UNIT FOR AIR, mbar, as Info and the HUD's AIR say it (QA F-118): \"mb\" read as megabytes to many players, and\n// nothing said what a number meant. Every air chip's tip ends with Earth's air for scale.\nconst mbText = (mb) => Math.round(mb).toLocaleString('en-US') + ' mbar';\nexport const EARTH_AIR = ' Earth\\u2019s air is about 1,013 mbar.';\nconst H = HAZARD;\n\n// THE AIR, as the suit takes it (hazard-engine.js evaluateHazards): no air, crushing, breathable, unbreathable or thin.\n// `sv` is the system view's survival summary: { mbar, o2, tempC, airless }.\nexport function airChip(sv) {\n  const mb = Math.round((sv && sv.mbar) || 0), n = mbText(mb);\n  if (!sv || sv.airless || mb < H.VACUUM_MBAR) {\n    return [mb > 0 ? 'No air · ' + n : 'No air', 'bad',\n      `No air: under ${H.VACUUM_MBAR} mbar your suit breathes from its ${Math.round(H.O2_RESERVE_S / 60)}-minute oxygen reserve. Go back up to orbit to refill it.` + EARTH_AIR];\n  }\n  if (mb >= H.CRUSH_MBAR) {\n    return ['Crushing · ' + n, 'bad',\n      `Crushing: over ${H.CRUSH_MBAR.toLocaleString('en-US')} mbar the pressure squeezes your suit and costs health every second.` + EARTH_AIR];\n  }\n  if (mb >= H.BREATHABLE_MBAR && ((sv && sv.o2) || 0) >= H.BREATHABLE_O2_FRACTION) {\n    return ['Breathable · ' + n, 'good',\n      `Breathable: ${H.BREATHABLE_MBAR} mbar or more with at least ${Math.round(H.BREATHABLE_O2_FRACTION * 100)}% oxygen. You breathe it, and your oxygen reserve refills.` + EARTH_AIR];\n  }\n  if (mb >= H.BREATHABLE_MBAR) {\n    return ['Unbreathable · ' + n, 'poor',\n      `Unbreathable: under ${Math.round(H.BREATHABLE_O2_FRACTION * 100)}% oxygen. Your suit scrubs it, and your oxygen reserve holds.` + EARTH_AIR];\n  }\n  return ['Thin air · ' + n, 'poor', `Thin air: under ${H.BREATHABLE_MBAR} mbar your suit scrubs it, and your oxygen reserve holds.` + EARTH_AIR];\n}\n\n// THE TEMPERATURE: inside the comfort band costs nothing; outside it the suit strains; 40 degrees past it is where the\n// thermal rate reaches 2 %/s, which the hazard engine calls the suit failing (THERMAL_PER_DEG 0.05).\nexport const SUIT_FAILS_PAST_C = 40;\n// …BUT THE CHIP TURNS RED WELL BEFORE THAT (QA F-30, F-92): red as soon as a full suit lasts under two minutes there,\n// which is 16.7 degrees out. Amber used to run all the way to 40 out, so a -46 °C night side that killed in under eighty\n// seconds wore the same amber as a chilly morning. No air is red although the reserve lasts five minutes; a cold that\n// kills faster than that cannot be the milder colour.\nexport const CHIP_RED_UNDER_S = 120;\nexport const CHIP_RED_PAST_C = 100 / (CHIP_RED_UNDER_S * H.THERMAL_PER_DEG);\nconst degOut = (t) => (t > H.COMFORT_MAX_C ? t - H.COMFORT_MAX_C : t < H.COMFORT_MIN_C ? H.COMFORT_MIN_C - t : 0);\nfunction tempTone(t) {\n  const x = degOut(t);\n  const tone = x === 0 ? 'good' : x < CHIP_RED_PAST_C ? 'poor' : 'bad';\n  return tone;\n}\n// HOW LONG A FULL SUIT LASTS AT t, in words (\"about 80 s\", \"about 3 min\"), from the engine's own rate. The tip used to\n// say \"you slowly lose health\" for a cold that killed in under a minute and a half (QA F-30, F-92).\nexport function suitLastsText(t) {\n  const rate = Math.min(H.THERMAL_MAX, degOut(t) * H.THERMAL_PER_DEG);\n  if (!(rate > 0)) return '';\n  const sec = 100 / rate;\n  return sec < 90 ? 'about ' + Math.max(5, Math.round(sec / 5) * 5) + ' s' : 'about ' + Math.round(sec / 60) + ' min';\n}\nconst degText = (v) => String(Math.round(v)).replace('-', '−');\nconst finite = (v) => typeof v === 'number' && Number.isFinite(v);\n// `t` is the world's temperature: ONE NUMBER ALWAYS (the owner, Oct 12: \"change world gen so the entire worlds are one\n// temperature — seasons only affects daylight/geometry\"). The chip is just \"24 °C\", toned by what the suit makes of it,\n// because that is what the suit meets wherever Land puts you, at any hour, in any season, on a world that turns or one\n// locked to its star alike. It used to lead with the ground where Land would put you at this hour and add the world's\n// range, or this season's (\"0 °C · −3 to 56 this season\", QA F-92 and World Seasons phase 1); there is no range left.\nexport function tempChip(t) {\n  const band = `${H.COMFORT_MIN_C} to ${H.COMFORT_MAX_C} °C`;\n  if (!finite(t)) return ['— °C', '', 'Its temperature cannot be read from here.'];\n  const tone = tempTone(t), lasts = suitLastsText(t);\n  const tip = (tone === 'good' ? `Inside your suit's comfort band (${band}): the temperature costs nothing.`\n    : tone === 'poor' ? `Outside your suit's comfort band (${band}): it strains, and a full suit lasts ${lasts}.`\n    : `Far outside your suit's comfort band (${band}): it cannot hold, and a full suit lasts only ${lasts}.`)\n    + ' The whole world is this temperature, day and night, all year.';\n  return [degText(t) + ' °C', tone, tip];\n}\n\n// THE SEASON CHIP (World Seasons phase 1, the owner's pick \"ui = chip\"): \"Northern summer · autumn in 9 h\", for the\n// hemisphere you would land in (from orbit) or stand in (on foot). `s` is the system view's seasonNow (system-view\n// seasonSummary); null, or a world with no seasons to speak of (under 1° of tilt, or no star), is no chip at all.\nconst capital = (w) => (w ? w[0].toUpperCase() + w.slice(1) : '');\nexport function soonText(ms) {\n  if (!finite(ms) || ms < 0) return '';\n  const min = ms / 60000;\n  if (min < 60) return Math.max(1, Math.round(min)) + ' min';\n  const h = min / 60;\n  if (h < 36) return Math.round(h) + ' h';\n  return Math.round(h / 24) + ' days';\n}\n// \"11 days\", \"2.8 days\", \"1 day\": as the view's Info writes a year (system-view fmtSec).\nexport function daysText(d) {\n  if (!finite(d)) return '';\n  const t = d >= 10 ? String(Math.round(d)) : String(Math.round(d * 10) / 10);\n  return t + (t === '1' ? ' day' : ' days');\n}\nexport function seasonChip(s) {\n  if (!s || !s.seasonal || !s.name || !s.hemisphere) return null;\n  const soon = soonText(s.nextInMs);\n  const text = `${capital(s.hemisphere)} ${s.name}` + (soon && s.next ? ` · ${s.next} in ${soon}` : '');\n  const yd = finite(s.yearDays) && s.yearDays > 0 ? s.yearDays : null;\n  const yText = yd == null ? '' : daysText(yd), qText = yd == null ? '' : daysText(yd / 4);\n  let tip = `Tilted ${Math.round(s.tilt)}°` + (yText ? `: a year here is one orbit, ${yText}, and each season about ${qText}.` : '.');\n  tip += ` ${s.locked ? 'Its sun rocks north and south over the year, and the' : 'The'} ${s.hemisphere} hemisphere, where you ${s.onFoot ? 'stand' : 'would land'}, is in ${s.name}`\n       + (soon && s.next ? `; ${s.next} starts in ${soon}.` : '.');\n  tip += ' A season changes the daylight and the sun\\u2019s path, never the temperature.';\n  return [text, '', tip];\n}\n\n// THE DAYLIGHT CHIP (World Seasons round 3, the owner's \"show = diagram\"): how much of today the sun is up where you\n// would land (from orbit) or stand (on foot), and which way the days are going: \"Daylight 31 of 54 min · getting longer\",\n// \"· getting shorter\"; round a solstice near a pole, the day with no sunset or the night with no dawn, and when it ends:\n// \"Polar night · sun back in 2 days\", \"Polar day · sunset in 3 days\". `d` is daylight.mjs daylightCourse's answer\n// (system-view daylightNow); null, a world with no tilt (under 1°), no star or no day (one locked to its star), is no\n// chip at all. A polar night is a caution: sunlight charges nothing there (energy.mjs), and it lasts days.\n// AS SHORT AS A CHIP MUST BE (tests/daylight-ui.test.mjs holds every one to 40 characters, which a phone's sheet fits on\n// one line beside nothing): whole minutes for a day under an hour and a half, hours (tenths under ten) for a longer one.\nfunction daylightParts(share, daySec) {\n  if (!finite(share) || !finite(daySec) || daySec <= 0) return null;\n  const s = Math.min(1, Math.max(0, share));\n  if (daySec < 5400) {\n    const D = Math.max(1, Math.round(daySec / 60));\n    let a = Math.round(s * daySec / 60);\n    if (s > 0) a = Math.max(1, a);\n    if (s < 1 && D > 1) a = Math.min(D - 1, a);\n    return { a: String(a), D: String(D), unit: 'min', long: 'minutes' };\n  }\n  const h = daySec / 3600, f = (v) => (h < 10 ? String(Math.round(v * 10) / 10) : String(Math.round(v)));\n  return { a: f(s * h), D: f(h), unit: 'h', long: 'hours' };\n}\nexport function daylightAmountText(share, daySec) {\n  const p = daylightParts(share, daySec);\n  return p ? `${p.a} of ${p.D} ${p.unit}` : '';\n}\nexport function daylightChip(d) {\n  if (!d || !finite(d.share) || !finite(d.daySec) || d.daySec <= 0) return null;\n  const where = d.onFoot ? 'Where you stand' : 'Where you would land';\n  const tilted = finite(d.tilt) ? ` The world is tilted ${Math.round(d.tilt)}°, and the hemisphere leaning toward its star has the long days.` : '';\n  if (d.polar === 'night') {\n    const t = soonText(d.changeInMs);\n    return [t ? `Polar night · sun back in ${t}` : 'Polar night', 'poor',\n      `${where}, the sun does not rise at all: a polar night, as the pole side of the world leans away from its star.`\n      + (t ? ` It rises again in about ${t}.` : '') + ' Sunlight charges nothing until then.' + tilted];\n  }\n  if (d.polar === 'day') {\n    const t = soonText(d.changeInMs);\n    return [t ? `Polar day · sunset in ${t}` : 'Polar day', '',\n      `${where}, the sun does not set: a polar day, as the pole side of the world leans toward its star.`\n      + (t ? ` It sets again in about ${t}.` : '') + tilted];\n  }\n  const amt = daylightAmountText(d.share, d.daySec), p = daylightParts(d.share, d.daySec);\n  const trend = d.trend === 'longer' ? ' · getting longer' : d.trend === 'shorter' ? ' · getting shorter' : '';\n  const turn = soonText(d.turnInMs);\n  let tip = `${where}, the sun is up ${p.a} of the ${p.D} ${p.long} of each day.`;\n  if (d.trend === 'longer') tip += ` The days are getting longer` + (turn ? `, until the longest in about ${turn}.` : '.');\n  else if (d.trend === 'shorter') tip += ` The days are getting shorter` + (turn ? `, until the shortest in about ${turn}.` : '.');\n  return ['Daylight ' + amt + trend, '', tip + tilted];\n}\n\n// WHO HOLDS IT, for the owner chip's tip.\nexport function ownerTip(kind, name) {\n  if (kind === 'mine') return 'Yours: you and the builders you choose can build here.';\n  if (kind === 'free') return 'Unclaimed: nobody owns it yet. Claim it to build here.';\n  return `${name || 'A player'}’s world: only its owner and their builders can build here.`;\n}\n\n// WATER AND AIR YOU CAN TAKE (the owner: \"border and text colored appropriately\"): a chip when the world's seas and lakes\n// are a liquid a thermos can dip (phase.mjs worldSeaOf: water, methane or nitrogen; not ice, not lava), and one when its\n// air can fill a gas canister (functions/lib/gas-canister.mjs canisterFill: any air at all).\n// `f` is the system view's { sea: { kind, name } | null, canister }.\nexport function waterChips(f) {\n  const out = [];\n  if (!f) return out;\n  const s = f.sea;\n  if (s && s.kind === 'liquid' && s.name) {\n    const short = String(s.name).replace(/^Liquid\\s+/i, '');\n    out.push([short + ' seas', 'water', `Its seas and lakes hold ${s.name}. Dip your thermos in one to fill it.`]);\n  }\n  if (f.canister) out.push(['Canister air', 'gas', 'Its air can fill a gas canister: each fill draws a fifth of a kube of it.']);\n  return out;\n}\n\n// HAZARDS THE AIR CHIP DOES NOT ALREADY NAME (it says \"Crushing\" itself, in red): lava seas, and chlorine in the air.\n// `f.cl` is chlorine's fraction of the air.\nexport function hazardChips(f) {\n  const out = [];\n  if (!f) return out;\n  if (f.sea && f.sea.kind === 'lava') out.push(['Lava', 'bad', 'Lava: it burns. Your suit pulls you back to the last ground you stood on.']);\n  if ((f.cl || 0) > 0 && !f.airless) {\n    out.push(['Corrosive air', 'bad', `Corrosive: ${Math.max(1, Math.round(f.cl * 100))}% chlorine in the air slowly degrades your suit and your health.`]);\n  }\n  return out;\n}\n\n// THE SIZE, read off the info panel's own lines (system-view bodyChars), which carry markup round their times.\nconst strip = (s) => String(s == null ? '' : s).replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\nexport function sizeFacts(chars) {\n  const out = { diameter: null, day: null, locked: false };\n  for (const raw of chars || []) {\n    const l = strip(raw);\n    let m = /^diameter:\\s*([\\d,.]+)\\s*kubes/i.exec(l);\n    if (m) { out.diameter = Number(m[1].replace(/,/g, '')); continue; }\n    // \"day: 2h 10m\", \"day: none, one side always faces its star\", \"day: 29m · one side always faces its planet\" (QA F-45),\n    // and the older \"solar day: — · tidally locked\" a cached readout may still carry.\n    m = /^(?:solar )?day:\\s*(.*)$/i.exec(l);\n    if (m) {\n      out.locked = /tidally locked|always faces/i.test(m[1]);\n      const d = m[1].replace(/·?\\s*(tidally locked|one side always faces its (star|planet))/i, '').replace(/^none,?/i, '').trim();\n      out.day = d && d !== '—' && d !== '-' ? d : null;\n    }\n  }\n  return out;\n}\n// \"Ø 64 kubes · day 2 h 10 min\" / \"· tidally locked\".\nexport function sizeLine(f) {\n  if (!f) return '';\n  const parts = [];\n  if (f.diameter > 0) parts.push(f.diameter + ' kubes across');\n  if (f.locked && !f.day) parts.push('no day: one side always faces its star');\n  else {\n    if (f.day) parts.push(f.day + ' day');\n    if (f.locked) parts.push('one side always faces its planet');\n  }\n  return parts.join(' · ');\n}\n\n// A GAS GIANT'S CARD (QA F-122): it said only \"Gas giant\", with no Land and no word why, and none of the chips a rocky\n// world gets, so a player who picked the most eye-catching thing in the system could not tell broken from not allowed.\n// Now one line says why (no surface), and chips give what Info already knew: its temperature, its air (crushing, all the\n// way down) and its moons, the part of it a player can use. `info` is the system view's focus info ({ chars,\n// composition }); `moons` the count when the host knows it better (else read off Info's \"5 moons\" line).\nexport function gasGiantFacts(info, moons) {\n  // A NEBULA'S YOUNG STARS (the owner's Black Holes and Nebulas picks, Oct 27, N1) are bodies like a giant to the system view, but\n  // no gas giant: no air to measure, no cloud tops, no moons. Each says what it is.\n  if (info && info.kind === 'Protostar') return { note: 'A star still forming in its cocoon of gas: nothing to land on.', moons: 0, tempC: null,\n    chips: [['Still forming', '', 'Gas is still falling onto it. It glows from that, and will not shine as a star for a long time yet.']] };\n  if (info && info.kind === 'T Tauri star') return { note: 'A young star in its birth disc: nothing to land on.', moons: 0, tempC: null,\n    chips: [['Birth disc', 'good', 'A ring of rubble round the star, where its worlds are still forming.']] };\n  const lines = ((info && info.chars) || []).map(strip);\n  let tempC = null, n = Number.isFinite(moons) ? moons : null;\n  for (const l of lines) {\n    let m = /^(?:average temperature|avg temp):\\s*(−?-?\\d+)/i.exec(l);\n    if (m) tempC = Number(m[1].replace('−', '-'));\n    m = /^(\\d+) moons?$/i.exec(l);\n    if (m && n == null) n = Number(m[1]);\n  }\n  const gas = ((info && info.composition && info.composition.sections) || []).find((s) => s && s.state === 'gases');\n  const chips = [];\n  if (gas && gas.mbar > 0) chips.push(['Crushing air · ' + mbText(gas.mbar), 'bad', 'A gas giant is air all the way down, thick enough to crush any suit long before there is anything to stand on.' + EARTH_AIR]);\n  if (tempC != null) chips.push([degText(tempC) + ' °C', '', 'Its average temperature, at the tops of its clouds.']);\n  if (n) chips.push([n + (n === 1 ? ' moon' : ' moons'), 'good', `Its moons: the rocky ones can be landed on and claimed. Up a level shows ${n === 1 ? 'it' : 'them'} round the planet.`]);\n  const note = 'No surface: gas giants can’t be landed on or claimed.' + (n ? ' Their moons can.' : '');\n  return { note, chips, moons: n || 0, tempC };\n}\n\n// WHO IS THERE. `peers` [{uid, ign, addr, walking, pos}] (the renderer's roster), `world` its address, `sys` its system's\n// \"galaxy.system\", `friends` a Set of friend uids. Players on the world are counted; only friends are named.\nexport function peopleOn(peers, world, sys, friends, me) {\n  const here = [], friendsHere = [], friendsNear = [];\n  const seen = new Set();\n  for (const p of peers || []) {\n    if (!p || !p.uid || p.uid === me || seen.has(p.uid)) continue;\n    seen.add(p.uid);\n    const a = String(p.addr || '').replace(/\\.0$/, '');\n    const fr = !!(friends && friends.has(p.uid));\n    if (a === world) { here.push(p); if (fr) friendsHere.push(p); }\n    else if (fr && sys && a.split('.').slice(0, 2).join('.') === sys && a.split('.').length >= 3) friendsNear.push(p);\n  }\n  return { count: here.length, friendsHere, friendsNear };\n}\nexport function peopleLine(sum) {\n  if (!sum || !sum.count) return '';\n  const names = sum.friendsHere.map((p) => p.ign || 'A friend');\n  const others = sum.count - names.length;\n  const lead = sum.count + (sum.count === 1 ? ' player here' : ' players here');\n  if (!names.length) return lead;\n  const shown = names.slice(0, 2), more = names.length - shown.length + others;\n  return lead + ' · ' + shown.join(', ') + (more > 0 ? ` and ${more} more` : '');\n}\n// THE FRIEND TO JOIN: one on the world first, else one elsewhere in its system.\n// THE ROW SAYS WHAT THE NAME TAG SAYS (QA F-79): a friend away from the keyboard reads \"(away)\" here as over their suit,\n// so a Join is not a surprise when they do not answer. Join stays: landing beside someone away is fine.\n// …AND WHETHER THEY ARE STANDING OR ORBITING (QA F-13): a friend in orbit has no footing, so \"Join\" for them is joining\n// them up there (navJoin), and the row says so before the press rather than after it.\nexport function friendToJoin(sum) {\n  if (!sum) return null;\n  const here = sum.friendsHere[0];\n  const p = here || sum.friendsNear[0];\n  if (!p) return null;\n  const ign = p.ign || 'A friend', orbit = !p.walking;\n  const where = here ? (orbit ? 'in orbit here' : 'here') : 'in this system';\n  return { uid: p.uid, ign, addr: String(p.addr || '').replace(/\\.0$/, ''), pos: p.walking && p.pos ? p.pos : null,\n           orbit, afk: !!p.afk, text: `${ign} is ${where}${p.afk ? ' (away)' : ''}` };\n}\n// BESIDE THEM: two cells along the face from where they stand (enterWalk takes the column and finds its ground).\nexport function besideHit(pos) {\n  if (!pos) return null;\n  const a = [Math.abs(pos.x), Math.abs(pos.y), Math.abs(pos.z)];\n  let ai = 0; if (a[1] > a[0]) ai = 1; if (a[2] > a[ai]) ai = 2;\n  const t = (ai + 1) % 3, k = ['x', 'y', 'z'][t];\n  const out = { x: pos.x, y: pos.y, z: pos.z };\n  out[k] += out[k] > 0 ? -2 : 2;   // toward the face's middle, so a friend at an edge does not send you over it\n  return out;\n}\n\n// THE CLAIM IN ONE LINE (claiminfo): owner, builders, and whether anyone but the owner may build.\nexport function claimLine(c, me) {\n  if (!c) return '';\n  const mine = !!me && c.owner === me;\n  const n = Array.isArray(c.builders) ? c.builders.filter((u) => u && u !== c.owner).length : 0;\n  const access = c.allowAllFriends ? 'friends build' : n ? 'shared' : 'private';\n  return [mine ? 'Yours' : (c.ownerIgn || 'A player'), n + (n === 1 ? ' builder' : ' builders'), access].join(' · ');\n}\n\n// WHAT YOU DID HERE (history): your own record of the world, { built, mined, at, prev } (app.js worldHist). Only for a\n// world you changed. `onIt`: you are standing on it now, so \"last visit\" is the one before this.\nexport function agoText(ms, now) {\n  const d = Math.max(0, (now || Date.now()) - ms), m = Math.floor(d / 60000);\n  if (m < 2) return 'just now';\n  if (m < 60) return m + ' min ago';\n  const h = Math.floor(m / 60);\n  if (h < 24) return h + ' h ago';\n  const days = Math.floor(h / 24);\n  return days === 1 ? 'yesterday' : days + ' days ago';\n}\nexport function historyLine(rec, now, onIt) {\n  if (!rec || !((rec.built || 0) > 0 || (rec.mined || 0) > 0)) return '';\n  const did = [];\n  if (rec.built > 0) did.push(`You built ${rec.built} kube${rec.built === 1 ? '' : 's'} here`);\n  if (rec.mined > 0) did.push(`${did.length ? 'mined' : 'You mined'} ${rec.mined}`);\n  const last = onIt ? rec.prev : rec.at;\n  return did.join(', ') + (last ? ' · last visit ' + agoText(last, now) : onIt ? ' · first visit' : '');\n}\n// The record's update for a visit: a new visit starts when the last sighting is over VISIT_GAP_MS old.\nexport const VISIT_GAP_MS = 30 * 60 * 1000;\nexport function visitRec(rec, now) {\n  const r = Object.assign({ built: 0, mined: 0, at: 0, prev: 0 }, rec || {});\n  if (!r.at || now - r.at > VISIT_GAP_MS) r.prev = r.at || 0;\n  r.at = now;\n  return r;\n}\n\n// THE BIOME BAR (biomebar) and the Biomes panel: each biome's colour and share of the surface, largest first, the\n// shares scaled to fill the bar. `hexOf(id)` is the Kubipedia's colour for a biome.\nexport function biomeSegments(biomes, hexOf) {\n  const list = (biomes || []).filter((b) => b && b.pct > 0);\n  const total = list.reduce((a, b) => a + b.pct, 0);\n  if (!(total > 0)) return [];\n  return list.map((b) => ({ id: b.id, label: String(b.label || b.id), pct: b.pct, w: (100 * b.pct) / total,\n                            hex: (hexOf && hexOf(b.id)) || '#95a5a6' }));\n}\nexport const pctText = (p) => `${p < 0.95 ? p.toFixed(1) : Math.round(p)}%`;\n\n// THE ANNOUNCEMENT (a11y): \"<name>, <category>, <air>, <main action>\".\nexport function cardSay(m) {\n  if (!m) return '';\n  const air = (m.chips || []).find((c) => /air|Breathable|Crushing|Unbreathable/.test(c[0]));\n  const main = (m.actions || []).find((a) => a.primary) || (m.actions || [])[0];\n  const strip2 = (s) => String(s || '').replace(/<[^>]*>/g, '').trim();\n  return [m.title, m.short || m.kind || '', air ? air[0] : '', main ? [strip2(main.label), strip2(main.tail)].filter(Boolean).join(' ') : '']\n    .filter(Boolean).join(', ');\n}\n\n// LAND ON DRY GROUND (the owner: \"the land action button should place the user on the closest dry spot near where the\n// crosshairs would sit\"). From the column (tu, tv) under the crosshair, the nearest column `dry(tu, tv)` accepts, ring by\n// ring outward and nearest first within a ring; columns are kept inside ±lim. The same column when it is dry already;\n// null when nothing within maxR is.\nexport function nearestDry(tu, tv, dry, lim, maxR = 24) {\n  const inside = (u, v) => Math.abs(u) <= lim && Math.abs(v) <= lim;\n  if (inside(tu, tv) && dry(tu, tv)) return [tu, tv];\n  for (let r = 1; r <= maxR; r++) {\n    const ring = [];\n    for (let du = -r; du <= r; du++) for (let dv = -r; dv <= r; dv++) {\n      if (Math.max(Math.abs(du), Math.abs(dv)) !== r) continue;\n      const u = tu + du, v = tv + dv;\n      if (inside(u, v)) ring.push([u, v, du * du + dv * dv]);\n    }\n    ring.sort((a, b) => a[2] - b[2] || a[0] - b[0] || a[1] - b[1]);\n    for (const [u, v] of ring) if (dry(u, v)) return [u, v];\n  }\n  return null;\n}\n\n// LAND FACING AN OPEN VIEW (the owner's play review, Oct 23, u3: \"Score nearby landing cells by sightline (nothing solid\n// within 6 kubes in front) and turn the camera towards the most interesting thing in view\"). A guest landed on Cottage\n// Moon looking straight into a tree trunk that filled a third of the screen: the landing faced the top of the world\n// (or, out on a face, back across it: QA F-85) whatever stood in front. Now the landing looks at the columns round the\n// spot (system-view.js openLanding casts a ray at eye height along each of LAND_HEADINGS headings from each) and these\n// two decide. A heading is OPEN when nothing solid is within LAND_CLEAR kubes of the eye; an open one that meets\n// something within LAND_RANGE (a tree, a building, a hill: things stand there) beats an empty horizon, water in view adds\n// a little, and a heading that runs off the face within LAND_CLEAR (a strip of ground under black sky) counts for little.\nexport const LAND_CLEAR = 6, LAND_RANGE = 24, LAND_HEADINGS = 16, LAND_NEAR = 3;\n// `hit`: kubes to the first solid thing along the ray (Infinity: none within range); `edge`: kubes until the ray leaves\n// the face (Infinity: it does not); `water`: liquid on the ground in view; `inward`: -1..1, how far the heading points\n// back across the face toward its centre (the F-85 rule, kept as the tie-break).\nexport function headingScore({ hit = Infinity, edge = Infinity, water = false, inward = 0 } = {}) {\n  if (hit < LAND_CLEAR) return 0;\n  if (edge < LAND_CLEAR) return 0.2 + 0.05 * inward;\n  const met = Number.isFinite(hit) && hit <= LAND_RANGE;\n  return 1 + (met ? 1 : 0) + (water ? 0.5 : 0) + 0.2 * Math.min(1, edge / LAND_RANGE) + 0.15 * inward;\n}\n// The best of the candidates: [{ du, dv, headings: [{ dir, score }] }] (du, dv: how far from the spot asked for). The\n// spot keeps its own column unless a nearby one sees better, each kube away costing a tenth of a point; null when no\n// heading anywhere is open, and the landing then faces as it always did.\nexport function pickLanding(cands) {\n  let best = null;\n  for (const c of cands || []) for (const h of c.headings || []) {\n    if (!(h.score > 0.25)) continue;\n    const s = h.score - 0.1 * Math.hypot(c.du, c.dv);\n    if (!best || s > best.s + 1e-9) best = { s, du: c.du, dv: c.dv, dir: h.dir, score: h.score };\n  }\n  return best;\n}\n\n// THE CARD'S FOLD (expand): open or closed, remembered on the device.\nexport const FOLD_KEY = 'kubicorCardMore';\n"],"mappings":"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","names":[],"file":"world-card.mjs"}