{
 "note": "Every number on this panel came out of a real run of the four scripts in pipeline/. The pictures shown are the SVG output of that run, inlined on the page.",
 "ranOn": "2026-09-09",
 "pillow": true,
 "scripts": [
  {
   "id": "social_frame",
   "file": "pipeline/social_frame.py",
   "title": "Render a branded social frame from a data file",
   "does": "Reads one JSON file of frame records and writes one image per record at 1080 by 1080. Four frame kinds. The copy lives in the data file, the brand values live in brandkit.py, and the script only decides where things sit.",
   "command": "python3 social_frame.py --data input/frames.json --out ../out/frames",
   "inputs": [
    "pipeline/input/frames.json, 6 frame records",
    "brandkit.py, the palette, the type stack and the spark mark"
   ],
   "outputs": [
    "6 PNG at 1080 x 1080",
    "6 SVG at 1080 x 1080",
    "frames-built.json, every file with its byte count"
   ],
   "result": "6 frames, 12 files",
   "show": [
    {
     "id": "frame-01",
     "kind": "announcement",
     "svg": "out/frames/frame-01.svg",
     "png": "out/frames/frame-01.png",
     "bytes": 22746
    },
    {
     "id": "frame-02",
     "kind": "stat",
     "svg": "out/frames/frame-02.svg",
     "png": "out/frames/frame-02.png",
     "bytes": 16888
    },
    {
     "id": "frame-03",
     "kind": "quote",
     "svg": "out/frames/frame-03.svg",
     "png": "out/frames/frame-03.png",
     "bytes": 21211
    },
    {
     "id": "frame-04",
     "kind": "event",
     "svg": "out/frames/frame-04.svg",
     "png": "out/frames/frame-04.png",
     "bytes": 21453
    },
    {
     "id": "frame-05",
     "kind": "stat",
     "svg": "out/frames/frame-05.svg",
     "png": "out/frames/frame-05.png",
     "bytes": 14313
    },
    {
     "id": "frame-06",
     "kind": "announcement",
     "svg": "out/frames/frame-06.svg",
     "png": "out/frames/frame-06.png",
     "bytes": 19597
    }
   ]
  },
  {
   "id": "derive_sizes",
   "file": "pipeline/derive_sizes.py",
   "title": "Derive the standard sizes, with the aspect handled properly",
   "does": "Five presets in two fit modes. A cover crop is anchored on a focal point rather than the middle, so a wide crop of a square keeps the headline. Enlargement is allowed up to 1.2 times and past that the image is padded rather than stretched, and the record says which.",
   "command": "python3 derive_sizes.py --src ../out/frames --out ../out/derived",
   "inputs": [
    "The 12 files in out/frames",
    "input/focals.json, where the important part of each frame sits"
   ],
   "outputs": [
    "60 derived files across 5 presets",
    "derivatives.json, the scale, crop box, padding and reason for every one"
   ],
   "result": "12 sources, 60 derivatives",
   "sourceCount": 12,
   "derivativeCount": 60,
   "presets": [
    {
     "name": "og",
     "w": 1200,
     "h": 630,
     "fit": "cover",
     "use": "Link preview on a page or a post"
    },
    {
     "name": "card",
     "w": 640,
     "h": 360,
     "fit": "cover",
     "use": "Card image in a listing"
    },
    {
     "name": "thumb",
     "w": 400,
     "h": 400,
     "fit": "cover",
     "use": "Square thumbnail in the asset index"
    },
    {
     "name": "portrait",
     "w": 1080,
     "h": 1350,
     "fit": "cover",
     "use": "Tall social placement"
    },
    {
     "name": "email",
     "w": 600,
     "h": 200,
     "fit": "contain",
     "use": "Header strip in an email, padded not cropped"
    }
   ],
   "policy": "enlarge by at most 1.20 times, otherwise pad rather than stretch",
   "show": [
    {
     "preset": "og",
     "use": "Link preview on a page or a post",
     "target": [
      1200,
      630
     ],
     "mode": "cover",
     "scale": 1.11111,
     "crop": [
      0,
      83.7,
      1080,
      567
     ],
     "pad": [
      0,
      0
     ],
     "focal": [
      0.5,
      0.34
     ],
     "notes": [
      "enlarged 1.11 times, inside the 1.20 limit",
      "48% of the source area was cropped away"
     ],
     "svg": "out/derived/og/frame-01.svg",
     "source": "frame-01.svg",
     "sourceSize": [
      1080,
      1080
     ]
    },
    {
     "preset": "card",
     "use": "Card image in a listing",
     "target": [
      640,
      360
     ],
     "mode": "cover",
     "scale": 0.59259,
     "crop": [
      0,
      63.45,
      1080,
      607.5
     ],
     "pad": [
      0,
      0
     ],
     "focal": [
      0.5,
      0.34
     ],
     "notes": [
      "44% of the source area was cropped away"
     ],
     "svg": "out/derived/card/frame-01.svg",
     "source": "frame-01.svg",
     "sourceSize": [
      1080,
      1080
     ]
    },
    {
     "preset": "thumb",
     "use": "Square thumbnail in the asset index",
     "target": [
      400,
      400
     ],
     "mode": "cover",
     "scale": 0.37037,
     "crop": [
      0,
      0,
      1080,
      1080
     ],
     "pad": [
      0,
      0
     ],
     "focal": [
      0.5,
      0.34
     ],
     "notes": [],
     "svg": "out/derived/thumb/frame-01.svg",
     "source": "frame-01.svg",
     "sourceSize": [
      1080,
      1080
     ]
    },
    {
     "preset": "portrait",
     "use": "Tall social placement",
     "target": [
      1080,
      1350
     ],
     "mode": "contain",
     "scale": 1,
     "crop": [
      0,
      0,
      1080,
      1080
     ],
     "pad": [
      0,
      135
     ],
     "focal": [
      0.5,
      0.34
     ],
     "notes": [
      "a cover crop would have enlarged the source 1.25 times, past the 1.20 limit, so it was padded instead",
      "padded by 0 by 135 pixels to reach the frame"
     ],
     "svg": "out/derived/portrait/frame-01.svg",
     "source": "frame-01.svg",
     "sourceSize": [
      1080,
      1080
     ]
    },
    {
     "preset": "email",
     "use": "Header strip in an email, padded not cropped",
     "target": [
      600,
      200
     ],
     "mode": "contain",
     "scale": 0.18519,
     "crop": [
      0,
      0,
      1080,
      1080
     ],
     "pad": [
      200,
      0
     ],
     "focal": [
      0.5,
      0.34
     ],
     "notes": [
      "padded by 200 by 0 pixels to reach the frame"
     ],
     "svg": "out/derived/email/frame-01.svg",
     "source": "frame-01.svg",
     "sourceSize": [
      1080,
      1080
     ]
    }
   ]
  },
  {
   "id": "contact_sheet",
   "file": "pipeline/contact_sheet.py",
   "title": "Build a contact sheet from a folder",
   "does": "Lays every image in a folder onto one page with its file name and pixel size under it. The vector sheet nests each source SVG inside the page, so it is the same artwork rather than a picture of it.",
   "command": "python3 contact_sheet.py --src ../out/frames --cols 3",
   "inputs": [
    "A folder of PNG or SVG files"
   ],
   "outputs": [
    "contact-sheet.png",
    "contact-sheet.svg"
   ],
   "result": "One page, 6 items, 16 kB as SVG and 59 kB as PNG",
   "show": [
    {
     "svg": "out/contact-sheet.svg",
     "png": "out/contact-sheet.png",
     "bytes": 16010
    }
   ]
  },
  {
   "id": "index_folder",
   "file": "pipeline/index_folder.py",
   "title": "Scan a folder and write an index",
   "does": "Walks a folder, reads the pixel size of every image from the PNG header and the SVG viewBox directly, groups files whose content is identical, and writes one file for a machine and one for a person. Needs nothing but the standard library, and records no modification times, so the same folder always produces the same index.",
   "command": "python3 index_folder.py --src ../out --ext .png,.svg",
   "inputs": [
    "Any folder"
   ],
   "outputs": [
    "index.json",
    "index.md"
   ],
   "result": "74 files, 882 kB, 7 folders, 0 duplicate groups",
   "totals": {
    "files": 74,
    "bytes": 903486,
    "human": "882 kB",
    "folders": 7,
    "byKind": {
     "raster": 37,
     "vector": 37
    },
    "duplicateGroups": 0,
    "duplicateFiles": 0
   },
   "sample": [
    {
     "path": "contact-sheet.png",
     "kind": "raster",
     "bytes": 60873,
     "width": 1044,
     "height": 912,
     "crc32": "aae11fd0"
    },
    {
     "path": "contact-sheet.svg",
     "kind": "vector",
     "bytes": 16010,
     "width": 1044,
     "height": 912,
     "crc32": "29b032eb"
    },
    {
     "path": "derived/card/frame-01.png",
     "kind": "raster",
     "bytes": 18544,
     "width": 640,
     "height": 360,
     "crc32": "e6b7de73"
    },
    {
     "path": "derived/card/frame-01.svg",
     "kind": "vector",
     "bytes": 2447,
     "width": 640,
     "height": 360,
     "crc32": "fffa0923"
    },
    {
     "path": "derived/card/frame-02.png",
     "kind": "raster",
     "bytes": 17146,
     "width": 640,
     "height": 360,
     "crc32": "f52da777"
    },
    {
     "path": "derived/card/frame-02.svg",
     "kind": "vector",
     "bytes": 2129,
     "width": 640,
     "height": 360,
     "crc32": "d58c9828"
    },
    {
     "path": "derived/card/frame-03.png",
     "kind": "raster",
     "bytes": 23541,
     "width": 640,
     "height": 360,
     "crc32": "bd6743a3"
    },
    {
     "path": "derived/card/frame-03.svg",
     "kind": "vector",
     "bytes": 2423,
     "width": 640,
     "height": 360,
     "crc32": "1c590828"
    }
   ]
  }
 ]
}