#!/usr/bin/env python3
"""Build the complete additive Edition 07 reader from the preserved R1 input.

Python 3.10+, standard library. This does not regenerate art, fetch external
content, modify the mod or publish a Website. Repeated builds are deterministic.
"""
from pathlib import Path
import copy,hashlib,json,re
from collections import Counter,defaultdict
from urllib.parse import urlsplit,unquote
from build_edition06 import Inventory,words,esc,slug,block_text,original_json_script,replace_json_script,insert_after_open,section_transform
from parse_substellar_blocks import blocks_from_text

HERE=Path(__file__).resolve().parent;ROOT=HERE.parent
EDITION='Workshop and Mars 07'
SOURCES=[('c07-review','review_direction_07.txt','P00','S1'),('c07-workshop','workshop_refinement_07.txt','P06/P07','S2'),('c07-mars','mars_campaign_07.txt','P04','S3')]
SLICES={'S1':('Review the complete direction','Current evidence, decisions, style references and meaningful reading paths.'),'S2':('Make workshop growth worthwhile','Three configurations, focused machinery and accountable worked orders.'),'S3':('Live the Mars campaign','Five regions, three complete outings, a useful yard and the connected investigation.')}
def read(n):return json.loads((HERE/n).read_text(encoding='utf-8'))
def js(d):return json.dumps(d,ensure_ascii=False,indent=2)+'\n'
def sha(b):return hashlib.sha256(b.encode('utf-8')if isinstance(b,str)else b).hexdigest()
def write(n,v):
    p=ROOT/n;p.parent.mkdir(parents=True,exist_ok=True);p.write_text(v,encoding='utf-8',newline='\n')

def main():
    old=(HERE/'reader_06_r1_preserved.html').read_text(encoding='utf-8');baseline=Inventory(old)
    old_manifest=read('edition06_r1_manifest_preserved.json');old_map=read('source_anchor_map_06.json')
    old_visuals=original_json_script(old,'visual-data');old_search=original_json_script(old,'search-data')
    assert len(baseline.ids)==4767 and len(baseline.source_blocks)==3131
    assert len(old_map['chapters'])==67 and len(old_visuals['records'])==84
    scope=read('scope_07.json');decisions=read('decisions_07.json');figures=read('figures_07.json')['figures']
    chapters=[];entries=[];new_ids=set();block_targets={};section_titles={}
    for key,filename,package,slice_id in SOURCES:
      text=(HERE/filename).read_text(encoding='utf-8');blocks=blocks_from_text(text);chapter=None;chapter_count=0
      for n,b in enumerate(blocks):
        bid=f'{key}-{n:04d}'
        if b[0]=='heading' and b[1]==1:
          chapter_count+=1;cid=f'{key}-{chapter_count:02d}';chapter=dict(id=cid,title=b[2],source_key=key,source_file='sources/'+filename,package=package,slice=slice_id,href='#'+cid,new_in_edition07=True,sections=[],blocks=[]);chapters.append(chapter);new_ids.add(cid)
        assert chapter is not None,filename
        target=bid
        if b[0]=='heading' and b[1]>1:
          match=re.match(r'^(C07-[A-Za-z0-9-]+)(?:\s|$)',b[2]);target=match.group(1)if match else cid+'--'+slug(b[2]);chapter['sections'].append(dict(id=target,title=b[2],level=b[1],href='#'+target,data_block=bid));section_titles[target]=b[2]
        assert target not in new_ids and target not in baseline.ids,target
        new_ids.add(target);block_targets[bid]=target;chapter['blocks'].append(dict(id=bid,target=target,value=b))
      entries.append(dict(key=key,file=filename,words=words(text),blocks=len(blocks),chapters=chapter_count,tables=sum(b[0]=='table'for b in blocks),sha256=sha((HERE/filename).read_bytes()),package=package,slice=slice_id,new_in_edition07=True))
    assert len(chapters)==8
    total_words=old_manifest['total_source_words']+sum(x['words']for x in entries)
    total_chapters=len(old_map['chapters'])+len(chapters)
    symbols=copy.deepcopy(old_map['canonical_symbol_anchors'])
    symbols.update({s:s for s in section_titles})
    # Add previously unexposed aliases without changing an inherited canonical mapping.
    for c in old_map['chapters']:
      for s in c.get('sections',[]):
        m=re.match(r'^(MA-[A-Z0-9-]+|WP05-PLAN-\d+|LE05-PLAN-\d+)\b',s['title'])
        if m:
          symbols.setdefault(m.group(1),s['id'])
          if '-PLAN-'in m.group(1):symbols.setdefault(m.group(1).replace('-PLAN-','-'),s['id'])
    for n in range(1,4):symbols.setdefault(f'MA-EXP-{n:02d}',f'C07-EXP{n}-01')
    for rid in ['MA-S1','MA-S2','MA-L1','MA-L2','MA-L3']:symbols.setdefault(rid,'C07-MARS-03')
    for f in old_visuals['records']+figures:symbols.setdefault(f['id'],f['target'])
    symbol_pattern=re.compile(r'\b(?:C07-[A-Za-z0-9-]+|C06-[A-Z0-9-]+|IND-M\d{2}|MAT\d{2}|PM\d{2}|IP\d{2}|SUP\d{2}|STA\d{2}|CEC\d{2}|REF\d{2}|EAR\d{2}(?:_H)?|MA-[A-Z0-9-]+|WP05(?:-PLAN)?-\d+|T1-DEC\d{2}|CD05-[A-Z]\d{2})\b')
    def source_link(s):
      t=symbols.get(s,s if s in new_ids else None)
      return f'<a href="#{esc(t.lstrip("#"))}"><code>{esc(s)}</code></a>'if t else f'<code>{esc(s)}</code>'
    def plain(s,links=True):
      if not links:return esc(s)
      out=[];pos=0
      for m in symbol_pattern.finditer(s):out.extend([esc(s[pos:m.start()]),source_link(m.group(0))]);pos=m.end()
      out.append(esc(s[pos:]));return ''.join(out)
    token_re=re.compile(r'(\*\*[^*]+\*\*|`[^`]+`|\[[^\]]+\]\([^\s)]+\))')
    def inline(s,links=True):
      out=[];pos=0
      for m in token_re.finditer(s):
        out.append(plain(s[pos:m.start()],links));t=m.group(0)
        if t.startswith('**'):out.append('<strong>'+plain(t[2:-2],links)+'</strong>')
        elif t.startswith('`'):out.append('<code>'+esc(t[1:-1])+'</code>')
        else:
          a=re.fullmatch(r'\[([^\]]+)\]\(([^)]+)\)',t);url=a.group(2)
          out.append(f'<a href="{esc(url)}">{esc(a.group(1))}</a>'if urlsplit(url).scheme in ('','http','https')else esc(t))
        pos=m.end()
      out.append(plain(s[pos:],links));return ''.join(out)
    def table(headers,rows,label,attrs=''):
      return f'<div class="table-wrap" tabindex="0" aria-label="{esc(label)}"><table {attrs}><thead><tr>'+''.join(f'<th scope="col">{h}</th>'for h in headers)+'</tr></thead><tbody>'+''.join(rows)+'</tbody></table></div>'
    def tr(cells,attrs=''):return f'<tr {attrs}>'+''.join('<td>'+c+'</td>'for c in cells)+'</tr>'
    def rowfilter(panel,rows):
      return f'<div class="c07-filter-panel" id="{panel}"><label>Find a record<input type="search" data-c07-filter placeholder="Name, ID, view or status" aria-label="Filter {panel.replace("-"," ")}"></label><p class="c07-filter-count" role="status" aria-live="polite">{len(rows)} records</p>'

    figures_at=defaultdict(list)
    for f in figures:
      assert all(s in new_ids for s in f['section_ids']),(f['id'],f['section_ids'])
      assert (ROOT/f['file']).is_file()
      figures_at[f['section_ids'][0]].append(f)
    rendered_figures=[]
    def figure(f):
      rendered_figures.append(f['id']);original=f.get('original',f['file']);diagram=f['file'].endswith('.svg')
      return (f'<figure class="plate companion-figure edition06-figure edition07-figure{" edition06-diagram"if diagram else ""}" id="{esc(f["target"])}" data-figure-id="{esc(f["id"])}">'
        f'<button type="button" data-image="{esc(f["file"])}" data-original="{esc(original)}" data-caption="{esc(f["title"])}" aria-label="Enlarge {esc(f["title"])}"><img src="{esc(f["file"])}" alt="{esc(f["alt"])}" loading="lazy" decoding="async"></button>'
        f'<figcaption><div class="plate-meta"><strong>{esc(f["title"])}</strong><span>{esc(f["id"])} · {esc(f["kind"])}</span></div><p>{esc(f["caption"])}</p><p class="figure-status"><strong>Status:</strong> {esc(f["status"])}</p>'
        '<details class="figure-limits"><summary>Scope, source and review limits</summary><p>'+esc('; '.join(f['not_established']))+'</p><p>Source identities: '+' · '.join(source_link(s)for s in f['source_ids'])+'</p><p>Related writing: '+' · '.join(source_link(s)for s in f['section_ids'])+'</p><p><a href="sources/figures_07.json">Current figure provenance</a></p></details>'
        f'<p class="figure-download"><a href="{esc(original)}" target="_blank" rel="noopener">Open full {"editable SVG"if diagram else "unchanged original"} ↗</a> · <a href="{esc(original)}" download>Download original</a></p></figcaption></figure>')
    new_search=[];chapter_html=[]
    for index,c in enumerate(chapters):
      attrs=lambda b:f'class="source-block" data-source="{esc(c["source_key"])}" data-block="{esc(b["id"])}" id="{esc(b["target"])}"'
      head=c['blocks'][0];out=[f'<section class="chapter edition06-chapter edition07-chapter" id="{c["id"]}" data-source-section="{c["source_key"]}" data-slice="{c["slice"]}"><header class="chapter-header"><span class="eyebrow">Edition 07 · {c["slice"]} · {c["package"]} · New chapter {index+1:02d}</span><h2 {attrs(head)}>{inline(c["title"],False)}</h2><div class="chapter-actions"><span class="pill overview">Creative documentation</span><button type="button" data-copy-anchor="{c["id"]}">Copy section link</button><a href="{c["source_file"]}" download>Exact text source</a><a href="#edition07-{c["slice"].lower()}">Slice context</a></div></header><div class="chapter-body">']
      current_section=None;active=None
      def close_list():
        nonlocal active
        if active:out.append(f'</{active}>');active=None
      def flush_figures():
        if current_section:
          for f in figures_at.get(current_section,[]):out.append(figure(f))
      for n,b in enumerate(c['blocks']):
        v=b['value'];new_search.append(dict(id=c['id']if n==0 else b['target'],chapter=c['title'],title=v[2]if v[0]=='heading'else section_titles.get(current_section,c['title']),text=block_text(v),source=c['source_key'],source_file=c['source_file'],block_id=b['id'],package=c['package'],slice=c['slice']))
        if n==0:continue
        kind=v[0]
        if kind not in ('bullet','number'):close_list()
        if kind=='heading':
          flush_figures();current_section=b['target'];level=min(v[1]+1,5);out.append(f'<h{level} {attrs(b)}>{inline(v[2],False)}<a class="anchor-mark" aria-hidden="true" tabindex="-1" href="#{b["target"]}">#</a></h{level}>')
        elif kind=='paragraph':out.append(f'<p {attrs(b)}>{inline(v[1])}</p>')
        elif kind in ('bullet','number'):
          tag='ul'if kind=='bullet'else'ol'
          if active!=tag:close_list();out.append(f'<{tag}>');active=tag
          out.append(f'<li {attrs(b)}>{inline(v[1])}</li>')
        elif kind=='table':
          rows=v[1];out.append(table([inline(x)for x in rows[0]],[tr([inline(x)for x in r])for r in rows[1:]],'Table in '+c['title'],attrs(b)))
        else:raise AssertionError(kind)
      close_list();flush_figures();out.append('</div>')
      out.append(f'<details class="review-box" data-chapter="{c["id"]}" data-title="{esc(c["title"])}"><summary>Your review notes <span class="review-state"></span></summary><div class="review-fields"><label>Direction for this chapter<select aria-label="Review direction for {esc(c["title"])}"><option value="">No review note</option><option value="Keep this direction">Keep this direction</option><option value="Question to resolve">Question to resolve</option><option value="Change requested">Change requested</option></select></label><label>Notes<textarea aria-label="Review notes for {esc(c["title"])}" placeholder="What should stay, change, or be explored further?"></textarea></label><span class="review-help">Notes stay in this browser. Export them to keep a portable copy.</span><button type="button" data-export-notes>Export all review notes</button></div></details>')
      prev=chapters[index-1]if index else dict(id='edition07',title='Edition 07 review entrance');nxt=chapters[index+1]if index+1<len(chapters)else dict(id='edition06-reconciliation-r1',title='Preserved R1 and earlier complete sources')
      out.append(f'<nav class="chapter-link-nav" aria-label="Adjacent chapters"><a href="#{prev["id"]}">← {esc(prev["title"])}</a><a href="#{nxt["id"]}">{esc(nxt["title"])} →</a></nav></section>');chapter_html.append('\n'.join(out))
    assert Counter(rendered_figures)==Counter({f['id']:1 for f in figures})

    routes=[]
    for r in read('reading_paths_07.json')['records']:
      links=''.join(f'<li><a href="#{s}">{esc(section_titles.get(s,s).split(" — ",1)[-1])}</a></li>'for s in r['steps'])
      routes.append(f'<article class="card c07-route" id="{r["id"]}"><h3>{esc(r["title"])}</h3><p>{esc(r["intro"])}</p><ol>{links}</ol></article>')
    slice_cards=[]
    for sid,(title,desc)in SLICES.items():
      cs=[c for c in chapters if c['slice']==sid];links=''.join(f'<li><a href="#{c["id"]}">{esc(c["title"])}</a></li>'for c in cs)
      slice_cards.append(f'<article class="card" id="edition07-{sid.lower()}"><span class="card-id">{sid} · Delivered documentation</span><h3>{title}</h3><p>{desc}</p><ul>{links}</ul></article>')
    hub=f'''<section class="section-wrap edition06-hub edition07-hub" id="edition07"><div class="section-head"><div><span class="eyebrow">Workshop &amp; Mars · Edition 07 · 2026-10-07</span><h2>A clearer way to review.<br>A workshop with purpose.<br>A complete Mars campaign.</h2></div><p>All earlier writing and artwork remain. Start with a reading path, inspect the changes or jump to one of the three completed documentation slices.</p></div>
<div class="edition06-stats"><div><strong>{total_words:,}</strong><span>Source words · 25 exact narratives</span></div><div><strong>75</strong><span>Chapters · 67 preserved</span></div><div><strong>102</strong><span>Visual identities · 84 preserved</span></div><div><strong>3</strong><span>Completed documentation slices</span></div></div>
<p class="overview-intro">This edition adds {sum(x['words']for x in entries):,} words in eight chapters, six selected concept boards, eight editable diagrams and four unchanged style references from the Website. The first-Moon workshop loop remains credited; Mars expands its existing five regions and three journey identities.</p>
<div class="edition06-actions"><a class="button primary" href="#edition07-review">Choose a reading path</a><a class="button" href="#edition07-decisions">Review decisions</a><a class="button" href="#edition07-style">Confirmed visual standard</a><a class="button" href="#edition07-coverage">Coverage and remaining work</a><a class="button" href="#edition07-sources">Source files and handoff</a></div>
<div class="intro-note"><strong>R1 incorporation is closed.</strong> SRC06-F01–03 were incorporated by the Website and recorded in the pinned maintainer closeout. Older pending labels remain historical. <a href="#C07-REV-03">Read the evidence boundary</a> · <a href="sources/evidence/website-r1-closeout/incorporation-receipt.md">Exact closeout record</a>. New gameplay implementation and public Website publication are separate from this source edition.</div>
<div class="c07-three">{''.join(slice_cards)}</div></section>
<section class="section-wrap" id="edition07-review"><div class="section-head"><div><span class="eyebrow">Start with a question</span><h2>Four useful reading paths.</h2></div><p>The following navigation, search and chapter notes work across the complete reader. Use each chapter’s review box to record what should stay or change, then export your notes.</p></div><div class="c07-two">{''.join(routes)}</div></section>'''
    decision_cards=[]
    for d in decisions['records']:
      decision_cards.append(f'<article class="card" id="c07-decision-{slug(d["id"])}"><span class="card-id">{esc(d["id"])} · {esc(d["status"])}</span><h3>{esc(d["title"])}</h3><p>{esc(d["direction"])}</p><a href="#{d["section_id"]}">Read the exact context →</a></article>')
    questions=''.join(f'<li><strong>{esc(q["question"])}</strong><p>{esc(q["recommended_default"])} <a href="#{q["section_id"]}">Review this section</a>.</p></li>'for q in decisions['optional_review_questions'])
    decision_hub=f'<section class="section-wrap" id="edition07-decisions"><div class="section-head"><div><span class="eyebrow">Approval stays specific</span><h2>Settled choices and reviewable recommendations.</h2></div><p>No new user decision blocks these documentation slices. Existing approvals retain their limits; new authored examples remain recommendations.</p></div><div class="c07-two">{"".join(decision_cards)}</div><details class="c07-details"><summary>Four optional questions for your next review</summary><ol>{questions}</ol></details><p><a href="sources/decisions_07.json">Download the decision record</a></p></section>'
    style_hub='''<section class="section-wrap" id="edition07-style"><div class="section-head"><div><span class="eyebrow">User-confirmed style references</span><h2>The original standard stays visible.</h2></div><p>The Tier 1 Moon scene is the primary benchmark. The forming workshop guides machinery. The four environment examples preserve pixel materials, substantial geometry and atmospheric depth.</p></div>
<div class="c07-two"><article class="card"><h3>REF03 · Primary visual standard</h3><button class="c07-reference-image" type="button" data-image="images/ref03.webp" data-original="images/originals/ref03.png" data-caption="REF03 — Primary style reference" aria-label="Enlarge the confirmed Tier 1 Moon style reference"><img src="images/ref03.webp" alt="Confirmed Tier 1 Moon scene: textured block terrain, white and orange equipment and cinematic light." loading="lazy"></button><p>Rich game materials, recognizable Minecraft forms and grounded atmosphere. <a href="#ref-ref03">Original reference and context</a>.</p></article><article class="card"><h3>CD05-A03 · Machinery standard</h3><button class="c07-reference-image" type="button" data-image="images/cd05-a03.webp" data-original="images/originals/cd05-a03.png" data-caption="CD05-A03 — Forming workshop style reference" aria-label="Enlarge the forming workshop style reference"><img src="images/cd05-a03.webp" alt="Forming workshop with substantial iron frames, copper fittings, readable stock and a service face." loading="lazy"></button><p>Working parts, varied iron/copper/wood and visible service access. <a href="#figure-cd05-a03">Preserved board and context</a>.</p></article></div>
<p><a href="#C07-REV-06">Read the complete visual standard and view the four environment examples</a> · <a href="sources/style_reference_07.json">Reference files, hashes and Website source pin</a>.</p><div class="intro-note"><strong>New image review stays honest.</strong> C07-A01–05 are functional studies with cleaner material treatment than the confirmed references; they remain reviewable. C07-A06 is the replacement after your style correction. All eight allowed calls are recorded; no additional generation is included.</div></section>'''

    machine_rows=[]
    for r in read('machine_relationships_07.json')['records']:
      e=r['edition07_review'];rid='c07-machine-'+slug(r['id']);search=' '.join([r['id'],r['name'],e['group'],e['participation']])
      machine_rows.append(tr([source_link(r['id'])+'<br>'+esc(r['name']),esc(e['group'])+'<br>'+esc(e['configuration_context']),esc(e['participation']),f'<a href="#{e["section_id"]}">Edition 07 relationship</a> · <a href="#{r["section_id"]}">Original operation card</a>'],f'id="{rid}" data-c07-row data-c07-search="{esc(search.lower())}"'))
      new_search.append(dict(id=rid,chapter='Edition 07 machine relationships',title=r['id']+' · '+r['name'],text=search,source='edition07-register'))
    machine_hub='<section class="section-wrap" id="edition07-machines"><div class="section-head"><div><span class="eyebrow">Complete responsibility map</span><h2>All 66 functions, with their place in the plan.</h2></div><p>Every canonical name and operation is retained. This overlay explains participation and optional scale; it does not create 66 mandatory separate blocks.</p></div>'+rowfilter('c07-machine-filter',machine_rows)+table(['Canonical responsibility','Context','Participation / host boundary','Writing'],machine_rows,'Edition 07 machine relationship matrix')+'</div><p><a href="sources/machine_relationships_07.json">Full original fields and recommendation overlay</a></p></section>'
    coverage=read('current_visual_coverage_07.json');coverage_rows=[]
    for r in coverage['records']:
      e=r['edition07_current'];rid='c07-view-'+slug(r['id']);search=' '.join([r['id'],r['subject'],r['family'],r['view'],e['current_status']]);links=' · '.join(f'<a href="#{f["target"]}">{esc(f["id"])}</a>'for f in e['related_figures'])or'No currently mapped representative view'
      coverage_rows.append(tr([f'<code>{esc(r["id"])}</code><br>'+esc(r['subject']),esc(r['view'])+'<br><small>Historical: '+esc(r['art_status'])+'</small>',esc(e['current_status'])+'<br><small>'+esc(e['coverage_class'])+'</small>',links],f'id="{rid}" data-c07-row data-c07-search="{esc(search.lower())}"'))
    remaining=''.join(f'<article class="card"><span class="card-id">{esc(r["id"])} · {esc(r["category"])}</span><h3>{esc(r["title"])}</h3><p>{esc(r["completion_or_activation_rule"])}</p><a href="#{r["section_id"]}">Read scope and evidence →</a></article>'for r in read('completion_backlog_07.json')['records'])
    coverage_hub=f'<section class="section-wrap" id="edition07-coverage"><div class="section-head"><div><span class="eyebrow">Completion has a defined scope</span><h2>What is covered, and what remains.</h2></div><p>The three selected slices are documented. This complete commissioning overlay keeps long-term detailed artwork distinct from the selected balanced overview.</p></div><p>All <strong>1,076</strong> historical view records are reconciled: 27 inherited delivered references/overviews, 12 previously delivered exact illustrative layouts, and 1,037 separately unfilled detailed views. Representative imagery is linked where its source IDs support that relationship. These are historical commissioning possibilities, including excluded packages; they are not 1,037 required tasks for this edition.</p><div class="c07-two">{remaining}</div><details class="c07-details"><summary>Inspect all 1,076 reconciled historical view records</summary>'+rowfilter('c07-coverage-filter',coverage_rows)+table(['Subject and original ID','Requested view','Current reading','Related representative artwork'],coverage_rows,'All historical artwork-view records with current context')+'</div></details><p><a href="sources/current_visual_coverage_07.json" download>Complete current visual coverage</a> · <a href="sources/completion_backlog_07.json" download>Remaining work and activation rules</a> · <a href="sources/coverage_07.json" download>Selected slice coverage</a></p></section>'
    figure_links=''.join(f'<li><a href="#{f["target"]}"><code>{f["id"]}</code> {esc(f["title"])}</a><span>{esc(f["kind"])}</span></li>'for f in figures)
    figure_hub=f'<section class="section-wrap" id="edition07-figures"><div class="section-head"><div><span class="eyebrow">New explanatory views</span><h2>Six concepts, eight diagrams and four style references.</h2></div><p>Each figure appears beside its writing, with original-file access, a stable ID and explicit limits. Existing art remains in its original context.</p></div><div class="edition06-figure-group"><ul>{figure_links}</ul></div><p><a href="#visual-directory">Browse all 102 visual records</a> · <a href="sources/image_generation_07.json">Eight-call generation and correction ledger</a></p></section>'
    register_names=sorted(set(read('coverage_07.json')['register_files']+['evidence_07.json','style_reference_07.json','image_generation_07.json','figures_07.json','workshop_orders_07.json','mars_campaign_07.json']))
    source_rows=[tr([f'<a href="sources/{e["file"]}" download>{e["file"]}</a>',e['slice']+' · '+e['package'],f'{e["words"]:,} words / {e["chapters"]} chapters',f'<code>{e["sha256"][:16]}</code>'])for e in entries]
    sources_hub=f'<section class="section-wrap" id="edition07-sources"><div class="section-head"><div><span class="eyebrow">Complete portable source</span><h2>Read, review, rebuild and hand off.</h2></div><p>All 25 narratives are complete text sources. JSON registers carry relationships and review status; exact images and editable diagrams stay in this folder.</p></div><div class="edition06-actions"><a class="button primary" href="sources/complete_direction_07.txt" download>Download complete text</a><a class="button" href="sources/edition-manifest.json" download>Current manifest</a><a class="button" href="sources/source_anchor_map_07.json" download>All chapter, section and symbol links</a><a class="button" href="sources/visual_catalogue_07.json" download>All 102 visuals</a></div>'+table(['New exact source','Slice','Scope','SHA-256 prefix'],source_rows,'New narrative sources')+'<details class="c07-details"><summary>Registers, verification and reproducible build</summary><ul class="edition06-file-list">'+''.join(f'<li><a href="sources/{n}" download>{n}</a></li>'for n in register_names)+'</ul><p><a href="sources/build_edition07.py">Reader builder</a> · <a href="sources/build_diagrams_07.py">SVG builder</a> · <a href="sources/reader_build_report_07.json">Build receipt</a> · <a href="sources/verification_07.json">Verification and actual limits</a> · <a href="sources/verify_edition07.py">Independent verifier</a> · <a href="README.txt">Package guide</a></p></details><div class="intro-note"><strong>Manual Website incorporation.</strong> The ready-to-send prompt and repository handoff in the delivery envelope pin this exact source snapshot. The Website agent preserves its current app, incorporates the new source relationships, retains the confirmed art standard and returns a revision-bound report. <a href="sources/website_handoff_07.md">Integration instructions</a> · <a href="sources/website_return_template_07.md">Return report template</a>.</div><p><a href="sources/reader_06_r1_preserved.html">Exact preserved R1 reader</a> · <a href="sources/edition06_r1_manifest_preserved.json">R1 manifest</a> · <a href="sources/baseline_06_r1_inventory.json">Complete 313-file baseline inventory</a>. Source commit identity belongs in the external delivery receipt, avoiding self-referential hashes.</p></section>'
    addition='\n'.join([hub,decision_hub,style_hub,figure_hub,machine_hub,coverage_hub,sources_hub,*chapter_html])
    marker='<section class="section-wrap edition06-hub" id="edition06-reconciliation-r1">';assert old.count(marker)==1
    result=old.replace(marker,addition+'\n'+marker,1)
    result=re.sub(r'<title>.*?</title>','<title>SUBSTELLAR — Workshop &amp; Mars · Edition 07</title>',result,count=1,flags=re.S)
    def hero_update(h):
      h=h.replace('Creative &amp; product direction · Selected completion edition 06','Creative &amp; product direction · Workshop and Mars edition 07',1).replace('href="#edition06">Open the current direction','href="#edition07">Open the current direction',1)
      h=h.replace('<b>111,411</b><span>Earlier source words preserved</span>','<b>148,817</b><span>Earlier source words preserved</span>',1).replace('<b>37,406</b><span>Words of selected completion</span>',f'<b>{sum(x["words"]for x in entries):,}</b><span>Words of new review, workshop and Mars</span>',1).replace('<b>84</b><span>Concepts, references and diagrams</span>','<b>102</b><span>Concepts, references and diagrams</span>',1)
      return h
    result=section_transform(result,'overview',hero_update)
    nav=[]
    for sid,(title,desc)in SLICES.items():
      nav.append(f'<details class="nav-group edition06-nav-group" open><summary>07 / {sid} · {title}</summary><div class="nav-chapter"><a href="#edition07-{sid.lower()}">{sid} overview</a></div>')
      for c in [x for x in chapters if x['slice']==sid]:
        nav.append(f'<div class="nav-chapter"><a href="#{c["id"]}"><span class="nav-num">07</span>{esc(c["title"])}</a><details><summary>Sections · {len(c["sections"])}</summary><div class="nav-sub">'+''.join(f'<a href="#{s["id"]}">{esc(s["title"])}</a>'for s in c['sections'])+'</div></details></div>')
      nav.append('</details>')
    def sidebar_update(s):
      prime='<a href="#edition07">Current direction · Edition 07</a><a href="#edition07-review">Reading paths and review</a><a href="#edition07-decisions">Decisions and recommendations</a><a href="#edition07-style">Confirmed visual standard</a><a href="#edition07-machines">66 machine relationships</a><a href="#edition07-coverage">Current coverage and remaining work</a><a href="#edition07-sources">Current sources and handoff</a>'
      s=s.replace('<nav class="nav-prime">','<nav class="nav-prime">'+prime,1).replace('href="#edition06">Current direction · Edition 06','href="#edition06">Preserved Edition 06 overview',1)
      return s.replace('</nav>','</nav>'+''.join(nav),1)
    result=section_transform(result,'sidebar',sidebar_update)
    for id in ['edition06-reconciliation-r1','edition06','edition06-decisions','edition06-sources','artwork-backlog','remaining-detail-05','next-image-slice','source-package']:
      note='<aside class="edition06-historical-note"><strong>Preserved edition context.</strong> Earlier current/pending wording records its original edition. Edition 07 credits the R1 closeout, activates the Mars campaign and adds a current review/coverage layer. <a href="#edition07">Current overview</a> · <a href="#edition07-style">Current visual standard</a> · <a href="#edition07-coverage">Current remaining work</a>.</aside>'
      result=insert_after_open(result,id,note)
    visual_records=copy.deepcopy(old_visuals['records'])+copy.deepcopy(figures)
    visual_catalogue=dict(edition=EDITION,records=visual_records,total_distinct_visuals=len(visual_records),unique_raster_concepts_and_references=56,editable_figures=46,new_generated_concepts=6,new_editable_diagrams=8,unchanged_style_references_imported=4,prior_visuals_preserved=84,primary_style_reference='REF03')
    def visual_update(s):
      kinds=sorted({f['kind']for f in figures});opts=''.join(f'<option value="{esc(k)}">{esc(k)}</option>'for k in kinds)
      rows=[tr([f'<code>{f["id"]}</code><br><a href="#{f["target"]}">{esc(f["title"])}</a>',esc(f['kind'])+'<br>'+esc(f['status']),esc(f['caption']),' · '.join(source_link(x)for x in f['source_ids'])],f'id="visual-row-{f["id"].lower()}" data-visual-row data-kind="{esc(f["kind"])}"')for f in figures]
      return s.replace('</select>',opts+'</select>',1).replace('</tbody>',''.join(rows)+'</tbody>',1).replace('<div class="backlog-controls">','<p class="edition06-catalogue-note">Edition 07 current catalogue: 102 visual identities. Earlier counts below are historical. <a href="#edition07-figures">View this edition’s additions</a>.</p><div class="backlog-controls">',1)
    result=section_transform(result,'visual-directory',visual_update)
    def chapter_update(s):
      cards=''.join(f'<article class="card chapter-guide-card" data-chapter-guide data-slice="{c["slice"]}"><span class="card-id">Edition 07 · {c["slice"]} · {c["package"]}</span><h3><a href="#{c["id"]}">{esc(c["title"])}</a></h3><p>{SLICES[c["slice"]][1]}</p><a class="guide-context" href="#edition07-{c["slice"].lower()}">Slice context →</a></article>'for c in chapters)
      opts=''.join(f'<option value="{sid}">Edition 07 · {sid} · {title}</option>'for sid,(title,desc)in SLICES.items())
      return s.replace('<div class="chapter-guide-grid">','<div class="chapter-guide-grid">'+cards,1).replace('</select>',opts+'</select>',1).replace('<div class="backlog-controls">','<p class="edition06-catalogue-note">All 75 complete chapters are indexed here: eight Edition 07 additions and 67 preserved earlier chapters.</p><div class="backlog-controls">',1)
    result=section_transform(result,'chapter-directory',chapter_update)
    for f in figures:new_search.append(dict(id=f['target'],chapter='Edition 07 figures',title=f['id']+' · '+f['title'],text=' '.join([f['caption'],f['status'],*f['source_ids']]),source='edition07-figure'))
    for id,title in [('edition07','Current direction: Workshop and Mars'),('edition07-review','Decision-led reading paths'),('edition07-decisions','Confirmed choices and recommendations'),('edition07-style','User-confirmed Tier 1 Moon and machinery visual standard'),('edition07-machines','66 machine responsibilities'),('edition07-coverage','Current coverage and remaining work'),('edition07-sources','Complete current source files and Website handoff')]:new_search.append(dict(id=id,chapter='Edition 07 review entrance',title=title,text=title,source='edition07-editorial'))
    search=old_search+new_search;result=replace_json_script(result,'search-data',search);result=replace_json_script(result,'visual-data',visual_catalogue)
    css='''<style id="edition07-style-rules">.c07-two,.c07-three{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px;margin:25px 0}.c07-three{grid-template-columns:repeat(3,minmax(0,1fr))}.c07-two .card,.c07-three .card{min-width:0}.c07-route ol{padding-left:22px}.c07-route li{padding:5px 0}.c07-route a,.c07-three a{line-height:1.75}.c07-details{border:1px solid var(--line);background:var(--panel);padding:18px 22px;margin:22px 0}.c07-details>summary{cursor:pointer;font-weight:600;color:var(--ink)}.c07-details>ol{padding-left:25px}.c07-details li p{font-size:13px;color:var(--muted)}.c07-filter-panel{margin:24px 0}.c07-filter-panel>label{display:block;font:12px var(--sans);color:var(--muted)}.c07-filter-panel input{display:block;margin-top:8px;max-width:560px;width:100%;padding:12px;background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:5px;font:15px var(--sans)}.c07-filter-count{font:12px var(--sans);color:var(--muted)}.c07-filter-panel td{font-size:12px;line-height:1.7;vertical-align:top}.c07-filter-panel td small{display:block;margin-top:5px;color:var(--muted)}.c07-filter-panel [hidden]{display:none!important}.c07-reference-image{width:100%;padding:0;border:1px solid var(--line);background:var(--panel);cursor:zoom-in}.c07-reference-image img{display:block;width:100%;height:auto}.edition07-chapter h3{scroll-margin-top:145px}.edition07-hub .section-head h2{font-size:clamp(28px,3vw,45px)}@media(max-width:1100px){.c07-three{grid-template-columns:1fr}}@media(max-width:720px){.c07-two{grid-template-columns:1fr}.c07-details{padding:14px}.c07-filter-panel .table-wrap{max-width:100%}}@media print{.c07-filter-panel>label,.c07-filter-count{display:none}.c07-two,.c07-three{display:block}.c07-two>.card,.c07-three>.card{break-inside:avoid;margin-bottom:15px}.c07-details{background:transparent}.c07-reference-image{border:none}}</style>'''
    result=result.replace('</head>',css+'</head>',1)
    result=result.replace('</body>','<script id="edition07-review-tools">document.querySelectorAll(".c07-filter-panel").forEach(function(panel){var input=panel.querySelector("[data-c07-filter]"),rows=Array.from(panel.querySelectorAll("[data-c07-row]")),count=panel.querySelector(".c07-filter-count");if(!input)return;input.addEventListener("input",function(){var q=input.value.trim().toLowerCase(),visible=0;rows.forEach(function(row){var show=!q||(row.dataset.c07Search||row.textContent.toLowerCase()).includes(q);row.hidden=!show;if(show)visible++});count.textContent=visible+" of "+rows.length+" records"})});</script></body>',1)
    final=Inventory(result)
    assert set(baseline.ids)<=set(final.ids)
    assert all(final.source_blocks.get(k)==v for k,v in baseline.source_blocks.items())
    assert not [k for k,n in final.ids.items()if n!=1]
    scripts=lambda t:re.findall(r'<script>(.*?)</script>',t,re.S)
    assert scripts(old)==scripts(result)
    assert re.search(r'<figure class="hero-art">.*?</figure>',old,re.S).group(0)in result
    assert all(s['id']in final.ids for s in search)
    assert all(f['target']in final.ids for f in visual_records)
    assert sum('data-chapter-guide'in t['attrs']for t in final.tags)==75
    assert sum('data-visual-row'in t['attrs']for t in final.tags)==102
    se=final.elements['sidebar'];sidebar=result[se['start']:se['end']]
    assert all(f'href="#{s["id"]}"'in sidebar for c in chapters for s in [c,*c['sections']])

    all_sources=copy.deepcopy(old_manifest['source_content'])+entries
    complete=['SUBSTELLAR — Creative and Product Direction\nWorkshop and Mars 07\n\nComplete exact narrative source collection. Separators are editorial. Current direction and status are in review_direction_07.txt and decisions_07.json; older text remains historical evidence.\n']
    for e in all_sources:
      p=HERE/e['file'];assert sha(p.read_bytes())==e['sha256'];t=p.read_text(encoding='utf-8');complete.append(f'\n\n===== BEGIN EXACT SOURCE: {e["file"]} | SHA-256 {e["sha256"]} =====\n\n'+t+f'\n\n===== END EXACT SOURCE: {e["file"]} =====\n')
    complete=''.join(complete)
    source_map=copy.deepcopy(old_map);source_map.update(edition=EDITION,chapter_count=75,chapters=copy.deepcopy(old_map['chapters'])+[{k:v for k,v in c.items()if k!='blocks'}for c in chapters],canonical_symbol_anchors=symbols,edition07_source_block_targets=block_targets,edition07_related_sections={r['id']:r['edition07_review']['section_id']for r in read('machine_relationships_07.json')['records']},new_section_count_07=len(section_titles),reader_sha256=sha(result),preserved_r1_mapping='source_anchor_map_06.json')
    source_map['figure_anchors']={f['id']:f['target']for f in visual_records}
    manifest=dict(title='SUBSTELLAR Creative and Product Direction',edition=EDITION,edition_id='workshop-mars-07',date='2026-10-07',date_timezone='America/Edmonton',site='index.html',reader_sha256=sha(result),previous_edition=dict(edition=old_manifest['edition'],source_commit='37e53c694a862411c161ec08da92c0294712959c',manifest='sources/edition06_r1_manifest_preserved.json',reader='sources/reader_06_r1_preserved.html',inventory='sources/baseline_06_r1_inventory.json'),source_content=all_sources,total_source_words=total_words,new_source_words=sum(e['words']for e in entries),prior_source_words_preserved=148817,total_chapters=75,new_chapters=8,source_file_count=25,source_block_count=len(final.source_blocks),total_distinct_visuals=102,prior_visuals_preserved=84,raster_visuals=56,editable_diagrams=46,new_concept_boards=6,new_editable_diagrams=8,unchanged_style_references_imported=4,new_generation_call_cap=8,new_generation_calls_used=8,generation_ledger='sources/image_generation_07.json',current_scope='sources/scope_07.json',current_decisions='sources/decisions_07.json',figure_register='sources/figures_07.json',visual_catalogue='sources/visual_catalogue_07.json',source_anchor_map='sources/source_anchor_map_07.json',complete_text='sources/complete_direction_07.txt',complete_text_sha256=sha(complete),primary_style_reference='REF03',machinery_style_reference='CD05-A03',verification='sources/verification_07.json',actual_mod_development_authorized=False,public_website_deployment_performed=False,source_commit_policy='Exact immutable source commit is in the external delivery envelope, not this self-hashed source folder.',notes=['Every R1 source block and original ID is retained.','Current recommendations do not approve numeric recipes, measured fit or runtime behavior.','C07-A01–05 are functional studies with material polish still reviewable; C07-A06 is the corrected environmental candidate.','Four user-selected Website style references are copied unchanged and consume no generation budget.'])
    readme=f'''SUBSTELLAR — WORKSHOP AND MARS 07
Complete documentation website · 2026-10-07

OPEN index.html and start at the Edition 07 entrance. Keep this folder together.
The reader also works offline. Direct anchors, the following sidebar/current-
reading bar, full-text search, image viewing, chapter notes and note export are
retained. New reading routes, machine filtering and current coverage are added.

CONTENT
{total_words:,} source words in 25 exact narrative files; 75 chapters.
All 148,817 R1 words, 67 chapters, 3,131 source blocks and 4,767 reader IDs remain.
102 visual identities: 84 preserved, six new concept boards, eight new SVG
diagrams and four unchanged user-selected Website style references. Original
PNGs, supplied Tier 1 references, source DOCX and prior editions are included.

THE THREE SLICES
S1: complete reference inventory, focused Workshop/Mars audit, current R1 closure,
    decision-led review routes, source evidence and 1,076-row visual overlay.
S2: compact/regional/frontier configurations, all 66 canonical responsibilities,
    focused power/protection/sample families and three accountable worked orders.
S3: existing five Mars regions, three complete journeys, six Frontier Yard stages,
    seven evidence steps and first PM03/PM04 application recommendations.

VISUAL STANDARD
REF03 Tier 1 Moon is the primary benchmark; CD05-A03 forming workshop guides
machinery. Four exact Website environment examples have source pins and hashes
in style_reference_07.json. Rich pixel materials, substantial Minecraft geometry
and cinematic light are appropriate. Clean toy-like forms and photoreal inventory
microdetail are not. A01–05 remain functional concept studies, not final finish
standards. A06 is the corrected environmental candidate. Eight calls of eight
were used, including the roof and user-directed style corrections. No remaining
generation budget, new shader feature or implementation is implied.

CURRENT FILES
sources/complete_direction_07.txt — every exact narrative, with source separators.
sources/edition-manifest.json — counts, input hashes and current artifact pointers.
sources/source_anchor_map_07.json — retained and additive anchors/relationships.
sources/visual_catalogue_07.json — all 102 identities.
sources/figures_07.json — new/imported figure metadata, scope and provenance.
sources/decisions_07.json — actual approvals, recommendations and review questions.
sources/coverage_07.json, current_visual_coverage_07.json,
  completion_backlog_07.json — current completion and remaining scope.
sources/verification_07.json — checks actually run and limits; read before claims.
sources/website_handoff_07.md, website_return_template_07.md — manual integration.

REBUILD
Python 3.10+, standard library. From any directory:
  python /path/to/substellar/sources/prepare_records_07.py
  python /path/to/substellar/sources/build_diagrams_07.py
  python /path/to/substellar/sources/prepare_figures_07.py
  python /path/to/substellar/sources/build_edition07.py
  python /path/to/substellar/sources/verify_edition07.py
Final selected images and supplied evidence are inputs; no generation/networking
occurs in these scripts. The reader starts from reader_06_r1_preserved.html,
never its own previous output. Older builders rebuild older editions only.

PRESERVATION AND LIMITS
Every R1 file remains unchanged except advancing root index.html, root README.txt
and sources/edition-manifest.json; their exact R1 bytes are separately retained
as reader_06_r1_preserved.html, README_06_r1.txt and
edition06_r1_manifest_preserved.json. The R1 inventory verifies all 313 inputs.
Old current/pending labels keep their historical context. SRC06-F01–03 are closed.
P04 is activated; P05, P08, P09, P12-C, P13, P14 and full P15 remain deferred.
Recipes, substitutions, material/ore equivalence, final Explorer/cargo fit,
durations, capacities and native behavior remain unverified where stated.
Null quantities mean unresolved, not free. First Mars arrival uses earlier
reachable equipment and supplies; PM03/PM04 improve later work. CEC01 is not
selected. No game BUILD or launcher development is authorized by this document.

DELIVERY
Use the external WEBSITE_AGENT_PROMPT.txt and delivery receipt for the exact
source commit and immutable snapshot path. Website integration/publication is a
manual separate step. This documentation source is not a live Website release.
'''
    report=dict(edition=EDITION,result='PASS',method='Deterministic additive HTML construction with separate exact-span inventory',prior_source_blocks_byte_preserved=3131,prior_reader_ids_preserved=4767,prior_chapters_preserved=67,new_source_files=3,new_chapters=8,new_sections=len(section_titles),new_source_words=sum(e['words']for e in entries),total_source_words=total_words,total_chapters=75,total_source_blocks=len(final.source_blocks),total_html_ids=len(final.ids),search_records=len(search),new_search_records=len(new_search),visual_records=102,new_figures=18,all_new_chapters_and_sections_in_sidebar=True,existing_inline_scripts_byte_preserved=True,hero_artwork_markup_byte_preserved=True,existing_storage_keys_unchanged=True,reader_sha256=sha(result),browser_interaction='See independent verification_07.json; not executed by this builder.',game_runtime='NOT RUN',public_website_publication='Not performed')
    outputs={'index.html':result,'README.txt':readme,'sources/edition-manifest.json':js(manifest),'sources/visual_catalogue_07.json':js(visual_catalogue),'sources/source_anchor_map_07.json':js(source_map),'sources/complete_direction_07.txt':complete,'sources/reader_build_report_07.json':js(report)}
    for n,v in outputs.items():write(n,v)
    broken=[];missing=[]
    for tag,attr,link in final.links:
      u=urlsplit(link)
      if u.scheme or u.netloc or link.startswith('//'):continue
      if u.path and not (ROOT/unquote(u.path)).is_file():missing.append(link)
      elif not u.path and u.fragment and unquote(u.fragment)not in final.ids:broken.append(link)
    assert not broken,sorted(set(broken))[:30]
    # A first build may precede the final verification receipt; final verification
    # requires these planned deliverables and rejects every missing local file.
    permitted={'sources/verification_07.json','sources/verify_edition07.py','sources/website_handoff_07.md','sources/website_return_template_07.md'}
    assert not set(missing)-permitted,sorted(set(missing)-permitted)
    report['unresolved_fragments']=[];report['pending_delivery_files']=sorted(set(missing));write('sources/reader_build_report_07.json',js(report))
    print(json.dumps(dict(result='PASS',chapters=75,words=total_words,new_words=manifest['new_source_words'],visuals=102,new_source_blocks=len(final.source_blocks)-3131,output=str(ROOT/'index.html')),indent=2))

if __name__=='__main__':main()
