p = 'backend/app/services/diagram.py'
s = open(p).read()

s = s.replace('''        if self.note:
            y += 4
            for line in self.note:
                out.append(f'<text x="{self.cx:.1f}" y="{y:.1f}" text-anchor="middle" '
                           f'font-size="{NOTE}" fill="{MUTED}">{escape(line)}</text>')
                y += NOTE * LINE''',
'''        if self.note:
            y += 4
            # The note follows the box, not the page. On a plain box that is
            # the muted grey; on the solid accent fill of a `key` box the same
            # grey is unreadable, which is exactly what it looked like.
            quiet = MUTED if self.tone == "plain" else ink
            faint = ' opacity=".85"' if self.tone != "plain" else ""
            for line in self.note:
                out.append(f'<text x="{self.cx:.1f}" y="{y:.1f}" text-anchor="middle" '
                           f'font-size="{NOTE}" fill="{quiet}"{faint}>{escape(line)}</text>')
                y += NOTE * LINE''')

s = s.replace('''    rows = [boxes[i:i + ROW_MAX] for i in range(0, len(boxes), ROW_MAX)]
    y = 0.0''',
'''    rows = [boxes[i:i + ROW_MAX] for i in range(0, len(boxes), ROW_MAX)]
    row_of = {id(box): number for number, row in enumerate(rows) for box in row}
    y = 0.0''')

s = s.replace('''        if a.y == b.y:
            parts.append(_arrow(a.x + a.width + 4, a.cy, b.x - 6, b.cy, label))
        else:
            # Down to the next row, from the end of one to the start of the next.
            parts.append(_arrow(a.cx, a.y + a.height + 3, b.cx, b.y - 6, label))''',
'''        # Which row a box is in, asked rather than guessed from its y. Boxes
        # of different heights are centred in their row, so two neighbours on
        # one line have different y values — inferring the row from that sent
        # every arrow after an uneven pair diagonally across the figure.
        if row_of[id(a)] == row_of[id(b)]:
            parts.append(_arrow(a.x + a.width + 4, a.cy, b.x - 6, b.cy, label))
        else:
            parts.append(_arrow(a.cx, a.y + a.height + 3, b.cx, b.y - 6, label))''')

s = s.replace('''    width = sum(box.width for box in boxes) + GAP_X * max(0, len(boxes) - 1)
    axis_y = 30.0''',
'''    # Room at the right for the head of the axis arrow, which is drawn past
    # the last tick and was clipping the edge of the figure.
    tail = 26.0
    width = sum(box.width for box in boxes) + GAP_X * max(0, len(boxes) - 1) + tail
    axis_y = 30.0''')

s = s.replace("""x2=\"{width:.1f}\" y2=\"{axis_y}\" '\n             f'stroke=\"{ARROW}\" stroke-width=\"2\"""",
              """x2=\"{width - 2:.1f}\" y2=\"{axis_y}\" '\n             f'stroke=\"{ARROW}\" stroke-width=\"2\"""")

s = s.replace('''def _arrow(x1: float, y1: float, x2: float, y2: float, label: str | None = None) -> str:''',
'''def _arrow(x1: float, y1: float, x2: float, y2: float, label: str | None = None,
           at: float = 0.5) -> str:''')
s = s.replace('''    mx, my = (x1 + x2) / 2, (y1 + y2) / 2''',
'''    # Where along the line the label sits. Halfway for a chain; further down
    # for a branch, where three labels halfway along a fan land on each other
    # and on the root box.
    mx, my = x1 + (x2 - x1) * at, y1 + (y2 - y1) * at''')
s = s.replace('''                    labels.get((ids[id(root)], ids[id(box)])))
             for box in children]''',
'''                    labels.get((ids[id(root)], ids[id(box)])), at=0.72)
             for box in children]''')

open(p, 'w').write(s)
print('written')
