{"compiledCode":"\"use strict\";Object.defineProperty(exports, \"__esModule\", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _react = require('react'); var _react2 = _interopRequireDefault(_react);\n\n function safeDestination(value) {\n  if (typeof value !== \"string\") return null;\n  try { const url = new URL(value.trim()); return !url.username && !url.password && [\"https:\", \"http:\"].includes(url.protocol) ? url.href : null; } catch (e) { return null; }\n} exports.safeDestination = safeDestination;\n function chooseNext(items, seen, previous, random = Math.random) {\n  let candidates = items.filter(item => !seen.has(item.id));\n  if (!candidates.length) { seen.clear(); candidates = items.filter(item => items.length === 1 || item.id !== previous); }\n  if (!candidates.length) return null;\n  const chosen = candidates[Math.floor(random() * candidates.length)];\n  seen.add(chosen.id); return chosen;\n} exports.chooseNext = chooseNext;\n async function loadDiscoveries(request = fetch, signal) {\n  const items = []; const ids = new Set(); let offset = 0;\n  for (let page = 0; page < 100; page++) {\n    const response = await request(`/extensions/cedros-directory/public/directories/main/listings?limit=100&offset=${offset}`, {credentials:\"omit\", signal});\n    if (!response.ok) throw new Error(\"The directory could not be loaded.\");\n    const payload = await response.json(); const data = _optionalChain([payload, 'optionalAccess', _ => _.data]);\n    if (_optionalChain([payload, 'optionalAccess', _2 => _2.ok]) !== true || !Array.isArray(_optionalChain([data, 'optionalAccess', _3 => _3.listings])) || typeof _optionalChain([data, 'optionalAccess', _4 => _4.pagination, 'optionalAccess', _5 => _5.hasMore]) !== \"boolean\") throw new Error(\"The directory returned an incomplete result.\");\n    for (const entry of data.listings) {\n      if (typeof _optionalChain([entry, 'optionalAccess', _6 => _6.id]) !== \"string\" || typeof _optionalChain([entry, 'optionalAccess', _7 => _7.title]) !== \"string\") throw new Error(\"The directory returned an incomplete listing.\");\n      const website = safeDestination(_optionalChain([entry, 'access', _8 => _8.contact, 'optionalAccess', _9 => _9.website]));\n      if (website && !ids.has(entry.id)) { ids.add(entry.id); items.push({...entry, publicFields:Array.isArray(entry.publicFields) ? entry.publicFields : [], website}); }\n    }\n    if (!data.pagination.hasMore) return items;\n    const next = data.pagination.nextOffset;\n    if (!Number.isSafeInteger(next) || next <= offset) throw new Error(\"The directory could not be read completely.\");\n    offset = next;\n  }\n  throw new Error(\"The directory is too large for this discovery view. Browse the directory instead.\");\n} exports.loadDiscoveries = loadDiscoveries;\nfunction Discovery() {\n  const [state, setState] = _react2.default.useState({loading:true, error:\"\", items:[] });\n  const [current, setCurrent] = _react2.default.useState(null);\n  const [attempt, setAttempt] = _react2.default.useState(0);\n  const seen = _react2.default.useRef(new Set());\n  _react2.default.useEffect(() => {\n    const controller = new AbortController(); let active = true;\n    setState({loading:true,error:\"\",items:[]}); setCurrent(null); seen.current.clear();\n    loadDiscoveries(fetch, controller.signal).then(items => { if(active) setState({loading:false,error:\"\",items}); }).catch(error => { if(active && error.name !== \"AbortError\") setState({loading:false,error:\"We could not load discoveries. Please retry or browse the directory.\",items:[]}); });\n    return () => { active=false; controller.abort(); };\n  }, [attempt]);\n  return _react2.default.createElement('section', { className: \"live-discovery\", 'aria-labelledby': \"discover-heading\",}\n    , _react2.default.createElement('h2', { id: \"discover-heading\",}, \"One good find at a time.\"     )\n    , _react2.default.createElement('div', { 'aria-live': \"polite\", 'aria-atomic': \"true\",}\n      , state.loading ? _react2.default.createElement('p', null, \"Loading the published directory…\"   ) : state.error ? _react2.default.createElement('p', { role: \"alert\",}, state.error) : !state.items.length ? _react2.default.createElement(_react2.default.Fragment, null, _react2.default.createElement('p', null, \"The directory is ready for its first approved listings.\"        ), _react2.default.createElement('p', null, \"There are no published entries with a public website to show yet. Every suggestion goes through editorial review.\"                 ), _react2.default.createElement('a', { href: \"/directory/submit\",}, \"Suggest a listing\"  )) : current ? _react2.default.createElement('article', null, _react2.default.createElement('h3', null, current.title), _react2.default.createElement('p', null, typeof current.summary === \"string\" ? current.summary : \"Visit the original site to explore this listing.\"), current.publicFields.filter((field) => field && /ownership|publisher|disclosure/i.test(String(field.label || field.key))).map((field,index) => _react2.default.createElement('p', { key: index,}, String(field.label || \"Disclosure\"), \": \" , String(_nullishCoalesce(field.value, () => ( \"\"))))), _react2.default.createElement('a', { href: current.website, target: \"_blank\", rel: \"noopener noreferrer\" ,}, \"Visit the original website ↗\"    ), typeof current.slug === \"string\" && _react2.default.createElement('p', null, _react2.default.createElement('a', { href: `/directory/listing?listing=${encodeURIComponent(current.slug)}`,}, \"Read the full listing and disclosures\"     ))) : _react2.default.createElement('p', null, \"Choose a surprise from the published directory. Placement, popularity, and payment do not affect the draw.\"               )\n    )\n    , state.error ? _react2.default.createElement('button', { type: \"button\", onClick: () => setAttempt(n=>n+1),}, \"Retry discoveries\" ) : state.items.length > 0 && _react2.default.createElement('button', { type: \"button\", onClick: () => setCurrent(chooseNext(state.items,seen.current,_nullishCoalesce(_optionalChain([current, 'optionalAccess', _10 => _10.id]), () => ( null)))),}, current ? \"Show me another\" : \"Show me a discovery\")\n    , _react2.default.createElement('p', null, _react2.default.createElement('a', { href: \"/directory\",}, \"Browse all published listings\"   ))\n  );\n}\n\n function ReadingPage(){return _react2.default.createElement('main', { className: \"site-reading\",}, _react2.default.createElement('p', { className: \"eyebrow\",}, \"StumbleBees\"), _react2.default.createElement('h1', null, \"The stumble\" ), _react2.default.createElement(Discovery, null), _react2.default.createElement('div', { dangerouslySetInnerHTML: {__html:\"\\n<p>Browse the live <a href=\\\"/directory\\\">directory</a>, try <a href=\\\"/stumble\\\">Stumble</a>, or <a href=\\\"/directory/submit\\\">suggest a business</a>. All submissions require editorial review. The reference material below describes the published standard; it is not evidence that an individual business has been checked. No public dataset export or automated check log is offered.</p>\\n<p>Read the schema and evidence requirements below, then use the live directory to browse published records or submit a suggestion. Examples in this guide are not real business listings.</p>\\n<h2>The published selection rules</h2>\\n<p>The button above draws from public, published entries with valid website links in the <a href=\\\"/directory\\\">directory</a>. Entries reach that pool only after editorial approval. The <a href=\\\"/business-register\\\">register guide</a> explains the evidence standard.</p>\\n<p>Examples are deliberately not seeded into the pool. A discovery surface whose contents did not clear <a href=\\\"/entry-bar\\\">the entry bar</a> would be a shuffled list of links rather than an implementation of this protocol.</p>\\n<ul>\\n<li><a href=\\\"/stumble/how-it-works\\\">Read how the rotation works</a></li>\\n<li><a href=\\\"/entry-bar\\\">See the entry bar</a></li>\\n</ul>\\n<h2>Required interaction</h2>\\n<p>Show you one business. If it is not for you, ask for another. That is the whole interaction, and it is intentionally the same interaction every time — there is no profile being built, no taste being learned and no feed being tuned.</p>\\n<p>Every result is an approved, published directory entry and links directly to the original site. Read the full listing for its evidence, check date, limitations, and ownership disclosures. Publication alone is not a guarantee of quality or current trading status.</p>\\n<h2>What it will never do</h2>\\n<p>The discovery surface has no field for ratings, payment, profiling or untraceable evidence, and money cannot change what it shows — see <a href=\\\"/no-money\\\">who pays for this</a>.</p>\\n<ul>\\n<li>Never frame the destination inside our interface. Framing strips an operator's analytics and breaks their consent flows, and it is not ours to do.</li>\\n<li>Never route your click through a redirect that hides where you are going. The link is a link.</li>\\n<li>Never auto-advance, trap your keyboard focus, or require a pointing device.</li>\\n<li>Never require an account, an email address or a login.</li>\\n</ul>\\n<h2>How it decides</h2>\\n<p>The rules are published in full at <a href=\\\"/stumble/how-it-works\\\">how the stumble works</a>. In short: only entries meeting the bar are eligible, the draw is genuinely random within that pool, repetition is avoided without profiling you, and nothing commercial can enter the calculation because there is nowhere for it to enter.</p>\\n<p>The curation happens before the shuffle, not inside it. That distinction is the whole argument of <a href=\\\"/blog/random-is-not-curated\\\">random is not the same as curated</a>.</p>\\n<h2>If you land on something broken</h2>\\n<p>It will happen. Every register decays between checks and ours is no exception. Under the <a href=\\\"/corrections\\\">corrections</a> procedure, a report starts a check sequence that is deliberately slow, so the entry may still be there a fortnight later. <a href=\\\"/blog/when-a-stumble-breaks\\\">What to do when a stumble lands on something broken</a> explains the whole loop.</p>\\n<h2>A note for search engines and agents</h2>\\n<p>A conforming discovery tool must provide keyboard access, explicit controls, no automatic advance and a bounded crawl surface. The control above runs only when you ask for a discovery. It never auto-advances.</p>\\n<p>It is also not an API. Please do not scrape it in place of a conforming export.</p>\\n<h2>Questions about the stumble</h2>\\n<h3>Is it actually random?</h3>\\n<p>Each unseen eligible listing has an equal chance in the next draw. When the pool is exhausted, a new round starts; an immediate repeat is avoided when more than one entry is available.</p>\\n<h3>Do you track what I click?</h3>\\n<p>The rules page states exactly what is and is not recorded, and we would rather over-disclose than under-disclose. What we do not do is build a profile of you, personalise the rotation, or use your behaviour to weight anything.</p>\\n<h3>Can a business get shown more often?</h3>\\n<p>No, and there is no mechanism by which anyone could ask. Frequency is not for sale and is not influenced by anything the business does.</p>\\n<h3>Why won't you show the site inside a frame like other tools?</h3>\\n<p>Because it takes something from the operator without asking: their analytics, their consent flows, and control over how their work appears. Some tools do it and the reading experience is genuinely nicer — we still think it is not ours to do.</p>\\n<h3>Does it work without a mouse?</h3>\\n<p>It has to be. A conforming stumble must make every action reachable by keyboard, never advance on its own, and never trap focus.</p>\\n<h3>Where are the stable directory records?</h3>\\n<p>Browse the directory for stable listing pages and search. Discovery changes only when you ask for another entry; it does not create a separate crawlable URL for each draw.</p>\\n<hr />\\n<p>A conforming surface must publish <a href=\\\"/stumble/how-it-works\\\">its rotation rules</a> before it operates, not after.</p>\\n<h2>Current implementation</h2>\\n<p>The discovery view reads the complete public listing pool when this page opens. It draws uniformly from entries with valid public website links, avoids repeats within a round, and starts a new round when exhausted. State stays in this page’s memory and resets on reload. No payment, featured flag, click history, account, or reader profile weights the draw.</p>\\n<p>Editors must unpublish an entry if its evidence is stale, disputed, or subject to removal. No automated check-log or age-based suspension is claimed. Reload to pick up publication changes. The view fails visibly rather than drawing from a partial pool if it cannot load all pages.</p>\\n\"},}), _react2.default.createElement('style', null, \".site-reading{max-width:1100px;margin:auto;padding:clamp(24px,5vw,64px) clamp(18px,4vw,40px);color:var(--c1biijfl-runtime-text,inherit);line-height:1.75;overflow-wrap:anywhere}.site-reading h1,.site-reading h2,.site-reading h3{font-family:var(--c1biijfl-font-heading,Georgia,serif);line-height:1.16}.site-reading h1{font-size:clamp(36px,6vw,64px);letter-spacing:-.035em}.site-reading h2{font-size:clamp(26px,4vw,34px);margin-top:2em}.site-reading p,.site-reading li{max-width:820px}.site-reading a{color:var(--c1biijfl-runtime-accent,inherit);text-underline-offset:4px}.site-reading a:focus-visible,.site-reading button:focus-visible{outline:3px solid currentColor;outline-offset:4px}.site-reading table{border-collapse:collapse;width:100%;display:block;overflow-x:auto}.site-reading th,.site-reading td{padding:14px;border:1px solid var(--c1biijfl-runtime-border,#b7b7ab);text-align:left;min-width:140px}.site-reading .live-discovery{padding:clamp(20px,4vw,40px);border:1px solid var(--c1biijfl-runtime-border,#b7b7ab);background:var(--c1biijfl-runtime-panel,transparent);border-radius:16px;margin-block:24px 40px}.site-reading .live-discovery h2{margin-top:0}.site-reading button{font:inherit;cursor:pointer;min-height:48px;padding:12px 22px;background:var(--c1biijfl-runtime-action-bg,#2d6545);color:var(--c1biijfl-runtime-action-fg,#fff);border:0;border-radius:8px}.site-reading blockquote{margin-left:0;border-left:3px solid currentColor;padding-left:22px}.site-reading .eyebrow{text-transform:uppercase;letter-spacing:.14em;font-size:12px;font-weight:700}\"));} exports.default = ReadingPage;"}