{
  "$comment": "Machine-readable companion to https://patterns.gf.cx/vehicle-page-structure/ — read from disk by ~/bin/pa_vehicle_spec.py, never fetched in-path. Published here so the structure is legible without a checkout.",
  "version": "1.2.0",
  "updated": "2026-08-12",
  "pattern": "https://patterns.gf.cx/vehicle-page-structure/",
  "reference_impl": "https://home.gf.cx/vehicles/ford-f250",
  "pages": [
    "LR4.html",
    "bmw-2013.html",
    "ford-f250.html"
  ],
  "defaults": {
    "details_open_on_load": false,
    "toggle_all_control": {
      "required": true,
      "host": "service-log",
      "note": "Must sit in a depth-1 <summary>. Authored deeper, it becomes unreachable the moment its own ancestors close — which is exactly what closing everything by default does.",
      "variant": "B · uppercase (chosen 2026-08-12 from an A/B/C specimen)",
      "specimen": "https://patterns.gf.cx/vehicle-page-structure/expand-all-control/",
      "type": {
        "family": "var(--sans)",
        "family_note": "NOT `font: inherit` — the control sits inside .log-heading, so inheriting gave it the editorial serif and made it the only UI chrome on the surface wearing the prose face.",
        "size_px": 9.5,
        "weight": 700,
        "transform": "uppercase",
        "tracking_px": 1.1,
        "pad_px": 11,
        "padding_rule": "padding-right = pad - tracking; letter-spacing adds a trailing space after the FINAL glyph, inside the padding, so symmetric padding renders right-heavy.",
        "icon": "drawn, not set — two bars in a box cap-height tall (0.73em), baseline-aligned, so it spans exactly what the capitals span. A font '+' centres on the maths axis and sits ~0.77px low against caps.",
        "icon_state": "minus = the same horizontal bar with the vertical dropped, driven off [aria-expanded=\"true\"] which the script already maintains.",
        "ink_gap_px": 11.0
      }
    }
  },
  "type_scale": {
    "section_label_px": 26,
    "nested_record_px": 20,
    "caption_px": 11.5,
    "note": "A nested record drops one step so it reads as a record within a section rather than a peer of it.",
    "control_px": 9.5
  },
  "order": [
    {
      "key": "service-log",
      "label": "Service log",
      "aliases": [
        "service log"
      ],
      "required": true,
      "hosts_toggle_all": true,
      "note": "Opens the page (v1.2.0, was second). It is the vehicle's primary record, and it hosts the expand-all control — which the reader wants before scrolling, not after the first section they might not care about.",
      "nests": "Individual service records — an inspection video, one invoice — belong inside this section, not beside it."
    },
    {
      "key": "seeking-contractors",
      "label": "Seeking contractors",
      "aliases": [
        "seeking contractors",
        "approved contractors"
      ],
      "required": false,
      "note": "A want-ad, not a record — it sits below the log it is looking for help with."
    },
    {
      "key": "vehicle-detail",
      "label": "Vehicle-specific detail",
      "aliases": [
        "installed accessories",
        "acquisition gallery",
        "current condition"
      ],
      "required": false,
      "slot": true,
      "note": "Open slot: what this particular vehicle has that the others do not."
    },
    {
      "key": "tco",
      "label": "Total cost of ownership",
      "aliases": [
        "total cost of ownership"
      ],
      "required": true
    },
    {
      "key": "documents",
      "label": "Documents",
      "aliases": [
        "documents"
      ],
      "required": true
    },
    {
      "key": "vehicle-reference",
      "label": "Vehicle-specific reference",
      "aliases": [
        "why classic plate",
        "classic registration"
      ],
      "required": false,
      "slot": true
    },
    {
      "key": "receipts-by-category",
      "label": "Receipts by category",
      "aliases": [
        "receipts by category"
      ],
      "required": false,
      "generated_by": "vehicle_receipts_render.py",
      "region": "RECEIPTS"
    },
    {
      "key": "qa",
      "label": "Q&A captures",
      "aliases": [
        "q&a captures",
        "q&amp;a captures"
      ],
      "required": false
    },
    {
      "key": "service-notes",
      "label": "Service notes",
      "aliases": [
        "service notes",
        "service maintenance notes"
      ],
      "required": true
    },
    {
      "key": "harvest-service-log",
      "label": "Harvest service log",
      "aliases": [
        "harvest service log"
      ],
      "required": true
    }
  ],
  "generated_regions": [
    {
      "name": "RECEIPTS",
      "start": "<!-- RECEIPTS:START -->",
      "end": "<!-- RECEIPTS:END -->",
      "generator": "vehicle_receipts_render.py",
      "owns": [
        "the by-category accordion",
        "the scan strip",
        "a <style>",
        "the category-filter <script>"
      ],
      "note": "The region owns more than markup, so a section reorder must move the whole region — not the <details> inside it."
    },
    {
      "name": "MILEAGE-CHART",
      "start": "<!-- MILEAGE-CHART-START -->",
      "end": "<!-- MILEAGE-CHART-END -->",
      "generator": "pa_vehicle_mileage_chart_inject.py",
      "flat": true,
      "note": "The closing plate is not an accordion — it is the one section that stays open, because it is the page's summary."
    }
  ],
  "guards": [
    {
      "name": "structure",
      "tool": "pa_vehicle_spec.py",
      "checks": [
        "all closed on load",
        "toggle-all reachable at depth 1",
        "relative section order",
        "required sections present"
      ]
    },
    {
      "name": "stale-dates",
      "tool": "pa_vehicle_mileage_chart_inject.py",
      "checks": [
        "a block whose label promises the future while every date inside it is past"
      ]
    }
  ]
}
