diff --git a/docs/examples/guide/widgets/checker04.py b/docs/examples/guide/widgets/checker04.py
index 54f2d7522..1243a3e6d 100644
--- a/docs/examples/guide/widgets/checker04.py
+++ b/docs/examples/guide/widgets/checker04.py
@@ -26,11 +26,11 @@ class CheckerBoard(ScrollView):
background: #004578;
}
CheckerBoard > .checkerboard--cursor-square {
- background: darkred
+ background: darkred;
}
"""
- cursor_square: var[Offset | None] = var(None)
+ cursor_square = var(Offset(0, 0))
def __init__(self, board_size: int) -> None:
super().__init__()
@@ -44,7 +44,7 @@ class CheckerBoard(ScrollView):
self.cursor_square = Offset(mouse_position.x // 8, mouse_position.y // 4)
def watch_cursor_square(
- self, previous_square: Offset | None, cursor_square: Offset | None
+ self, previous_square: Offset, cursor_square: Offset
) -> None:
"""Called when the cursor square changes."""
@@ -57,12 +57,10 @@ class CheckerBoard(ScrollView):
return region
# Refresh the previous cursor square
- if previous_square is not None:
- self.refresh(get_square_region(previous_square))
+ self.refresh(get_square_region(previous_square))
# Refresh the new cursor square
- if cursor_square is not None:
- self.refresh(get_square_region(cursor_square))
+ self.refresh(get_square_region(cursor_square))
def render_line(self, y: int) -> Strip:
"""Render a line of the widget. y is relative to the top of the widget."""
diff --git a/docs/guide/widgets.md b/docs/guide/widgets.md
index 96b9cbff1..9470d123f 100644
--- a/docs/guide/widgets.md
+++ b/docs/guide/widgets.md
@@ -339,3 +339,44 @@ We also need to compensate for the position of the horizontal scrollbar. This is
--8<-- "docs/images/scroll_view.excalidraw.svg"
+
+### Region updates
+
+When you call the [refresh][textual.widget.Widget.refresh] method it will refresh the entire widget.
+The Line API makes it possible to refresh parts of a widget, as small as a single character.
+Refreshing smaller regions makes updates more efficient, and keeps your widget feeling responsive.
+
+To demonstrate this we will update the checkerboard to highlight the square under the mouse pointer.
+Here's the code:
+
+=== "checker04.py"
+
+ ```python title="checker04.py" hl_lines="18 28-30 33 41-44 46-63 74 81-92"
+ --8<-- "docs/examples/guide/widgets/checker04.py"
+ ```
+
+=== "Output"
+
+ ```{.textual path="docs/examples/guide/widgets/checker04.py"}
+ ```
+
+We've added a style to the checkerboard which is the color of the highlighted square, with a default of "darkred". We will need this when we come to render the highlighted square.
+
+We've also added a reactive variable called `cursor_square` which will hold the coordinate of the square underneath the mouse. Note that we have used [var][textual.reactive.var] which gives as reactive superpowers but won't automatically refresh the whole widget, because we want to update only the squares under the cursor.
+
+The `on_mouse_move` handler takes the mouse coordinates from the [MouseMove][textual.events.MouseMove] object and calculates the coordinate of the square underneath the mouse. There's a little math here, so let's break it down.
+
+- The event contains the coordinates of the mouse relative to the top left of the widget, but we need the coordinate relative to the top left of board which depends on the position of the scrollbars.
+We can make this conversion by adding `event.offset` to `self.scroll_offset`.
+- Once we have the board coordinate underneath the mouse we divide the x coordinate by 8 and divide the y coordinate by 4 to give us the coordinate of a square.
+
+If the cursor square coordinate calculated in `on_mouse_move` changes, Textual will call `watch_cursor_square` with the previous coordinate and new coordinate of the square. This method works out the regions of the widget to update and essentially does the reverse of the steps we took to go from mouse coordinates to square coordinates.
+The `get_square_region` function calculates a [Region][textual.geometry.Region] object for each square and uses them as a positional argument in a call to [refresh][textual.widget.Widget.refresh]. Passing Regions to `refresh` tells Textual to update only the cells underneath those regions, and not the entire region.
+
+!!! note
+
+ Textual is smart about performing updates. If you refresh multiple regions (even if they overlap), Textual will combine them in to as few non-overlapping regions as possible.
+
+The final step is to update the `render_line` method to use the cursor style when rendering the square underneath the mouse.
+
+You should find that if you move the mouse over the widget now, it will highlight the square underneath the mouse pointer in red.
diff --git a/docs/images/scroll_view.excalidraw.svg b/docs/images/scroll_view.excalidraw.svg
index 0d3ba66a8..4dfc06120 100644
--- a/docs/images/scroll_view.excalidraw.svg
+++ b/docs/images/scroll_view.excalidraw.svg
@@ -1,6 +1,6 @@
-
+
- eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nN1da1NcIsuy/b5/xcScj3dTp96VdVwibtzw/cQ3Prj3hMGjXHUwMDExtFx1MDAwNYRG0Vx1MDAxM/u/30x0S/NoXHUwMDA0RaZcdTAwMDdnwlx1MDAxOWnAomtV5lpZmVn/+ePHj5/RczP4+a9cdTAwMWY/g26pXHUwMDEw1sqtwtPPP+nxx6DVrjXqeEn2fm43Oq1S75nVKGq2//XPf95cdTAwMTdad0HUXGZcdTAwMGKlgD3W2p1C2I465VqDlVx1MDAxYff/rEXBfft/6PtB4T7472bjvlx1MDAxY7VY/5dkgnItarRef1dcdTAwMTBcdTAwMDb3QT1q47v/L/7848d/et9jo2tcdTAwMDWlqFC/XHSD3lx1MDAwYnqX+lx1MDAwM7RCXGY/etCo91x1MDAwNmtcdTAwMDRcYrDGqPcn1Nrr+OuioIxXKzjkoH+FXHUwMDFl+nm6X1x1MDAxMfXgtntcXFrNtM787bZpXm/1f2ulXHUwMDE2hqfRc/h6J1xupWqnXHUwMDE1XHUwMDFiUztqNe6Ci1o5quJ1MfT4++vaXHK8XHT9V7VcdTAwMWGdm2o9aLdcdTAwMDde02hcdTAwMTZKtehcdTAwMTlcdTAwMWZT/P3B13vwr1x1MDAxZv1HuvhTXHUwMDA2JDNSXGIvXHUwMDE0SK3k+0V6tVx1MDAwNmDaOKe4kpJ7I9TQsNZcdTAwMWEhTlx1MDAwNFx1MDAwZetcdTAwMWa899VcdTAwMWZYsVC6u8HR1cv950hcdMXA9p/z9PZhrWVcXDtntNRcdTAwMDYs9/r9XHUwMDE51aB2U43wKU4yUMZya7nkXHUwMDAw0Fx1MDAxZmc76M2HXHUwMDE0Smu87t37XHUwMDE1+vXNnXJcdTAwMGZcdTAwMWH/jt+xevntjtU7YdhcdTAwMWYxXdiIwan/mk6zXFx4nXZhcZjaWqO89u/Xw1r9bvjtwkbpro+U3qN//flcdIQ6gCSEXG6rXHUwMDA1N8JyNzVE13OVu9vuysFDVl1cdTAwMWOfVs5cdTAwMWXzufWVlEPUWCaMdlZp6Vx1MDAxNN5cdTAwMGVvR0GqlDdKIE6VXHUwMDEzNrUgxeFr77k1S4ZR62RcdTAwMTJGvZPOOyfF1Fx1MDAxMFxyzrZ38e5cdTAwMTWLZX1ylN2Bvb2jO51yiGaEYlxceDBeOHRcdTAwMWFcdTAwMDBiXHUwMDEwo8Yqpi1wvCQk6OFxpVx1MDAwN6HCKFx1MDAwNfhHLFx1MDAxYkKtT0Ko4Fx1MDAxYX1cdTAwMDc6uOlcdTAwMWT95WHxYO/KQLFxdHxfuHzcRn7T/rVcdTAwMTBcdTAwMTX8QzOqmTPIZyRY4dFzuFx1MDAwMYRa5Vx1MDAxOZJcdTAwMDDv0UJxo41MLUSlcE7jUJdcdTAwMGWikExFJTo9iHObj1x1MDAxMHqfyVx1MDAxN9RmaSV3mq/eXHUwMDA169Wjja1QpN2IgmDIQjUoK1x1MDAwMVdlzEz2XHUwMDEwqjlDXHUwMDBiqo1cdTAwMTZcdTAwMWGXqzOpRahwOF1cdTAwMDZ9wNJxUZPMRVx1MDAwMY1cdTAwMDbeL+Gnxqh3XHUwMDA1a8LG7W2nlFx1MDAxM9dhp9w4dFx1MDAwNynHqJCGeYuk21x0hICPXHSiVzdvkIpcIlx1MDAxZlx1MDAwN6k0V7H5T1x1MDAxZER7r1x1MDAxNchXXHUwMDE2XHUwMDA1UcNcdTAwMTdcdTAwMDNRp1x1MDAxMiFquHWOm1x1MDAxObho62W3uuI2XHUwMDBljNjeVkFm/WXzqHqedkePc4ueXHUwMDFlOZyTXHUwMDA2/zUwKOpcdTAwMTG5TFx1MDAwMmp5a6X2UqaZjHJcdTAwMTDgOF8+kNpEkHortOFuejO6ooo+ut95gUZ1+3QjdyFcbuf1tEv6jGecK1xcjrhcdTAwMTjR43tcdTAwMDdDXHUwMDEwNUxJnFxyp1xyisf0XHUwMDAyVDiLmlx1MDAxZd9nyVx1MDAwMGpcdTAwMWRPXHUwMDAyqORCXGJcdTAwMDAuzdRcYl31uuGj6G612L6T97Ys2lf1ZspcdTAwMTEqvGFCO8GFVlx1MDAxNGSTg55eg0VscPyHW4n+Xuj0glQhfsDhq5dcZqSoXHUwMDE0kkDqrFx1MDAwMudR006N0b2wW12vqnw24kG4YZ+ew2b3LOVcdTAwMThVSjFkmtygI+dg5XDwXHUwMDFlIYomXHUwMDE2QeG4wmWb3rgorjFkI9yohVx1MDAwNUZcdTAwMTfl6GWio/fKI7nhfHqImruNsFiuPG6ePlx1MDAxYV7OX4bX9Y0w5Vx1MDAxMJVWM1xcirhcdTAwMTiFxvvOzVx1MDAwMESNXHUwMDE1XGZcdTAwMTC6WlJYSqbYhlx1MDAxYeuExPlaPoAmRu6VJf+mZ7Cha3f6Klx1MDAxMrflm9xBqXX+1FK5aqGQdrVERlRrb71CXHUwMDA0XGLBXHUwMDA3mShcdTAwMDWdhLTgaG9UgE0xRJ1Sgr4tXHUwMDFkRGPbm8NiSeJQXHUwMDE0LsxcdTAwMTmM6O3V9u6+y1x1MDAxZK7ooCF3XHUwMDBmz8+beZd2jKIoZFxiXHUwMDBlUNYjIVVqXHUwMDA0pJKhVkJcdTAwMDIgUTnFWFHqMIqLXGaQqCxuk35RVFQkXHUwMDBieuO1XHUwMDE2XHUwMDFh3PRRJ7m94k7C3Vr+ca15e3Mo7yv8Np9yR6+5YlaiLvRee4G6fcjRI1xykFxiXHKuwEu0temFqDXgtDNu2cwoqMRcdTAwMWRQdIHOOWWnt6JcdTAwMDfR9WNN33XPm6tcdTAwMGb5leg5e3pS3fhcclx1MDAxMKqNoHwmg35SqkFFj/yOIW5p+oFTvklqIUqpMM5cdL10XGLliVxcVChtaNOFT79Hf7X/tFWsdithXHUwMDA3Lnbq/mKtUT+8SrujR6bJrFx1MDAwM4lKSVgwUrshjDrGJeDMIHgokSi1XHUwMDE4ldwrNKPo/JZcZqRxXGJcdTAwMGVHRilcdTAwMWNMSnZ6O1x1MDAxYV695Fx1MDAwZm3JXHUwMDFj39unenS4d3/bujxOuVx1MDAxZM1cdTAwMDBKXCLLlVx1MDAwMKUoZjNcdTAwMDRRz5lcdTAwMTNcdTAwMTZcci0uV1x0Or2CSVx1MDAxYiW9VVotXHUwMDE5QsHoJIRcbpDOaS9nSFx1MDAxOL15Km2e19pcdTAwMDKuc8+7taq2p/mNX1x1MDAxY1x1MDAxN51cItXJoVx1MDAxZVJIylx1MDAxMYOgIY6PV4xcbmRcdTAwMDLW49RcYlT8MVx1MDAxYpU2jOJFXHUwMDAwpWHpNkCTMUrcxlo7/e5S0K7o0tnZntlrrjVtPtupblx1MDAxNLfTbkNcdTAwMDVnyDQ9WCFcciXcxXxcdTAwMDa9XHUwMDAxIHhQRqHYR8mM2EkvXHUwMDE5XHUwMDA1XHUwMDBijvtlo6JcdTAwMGWSs0gkTlx0cjA1vZ73PKxGneKWXHUwMDBi9ss1nr0sdTL7jZQjXHUwMDE0TShcbkLpXHJyUePju730cq8581riWtWSq1x1MDAxNKeQoI5HKqKWL25cdTAwMWZcdTAwMGInjdBQXHScWzmDXHQtNqriprW7d/+yenR/89jcPbzcyKZcdTAwMWOgXHUwMDE5XHUwMDA3zKGrQP9ccsg00ZBcdTAwMGUhVDHS+OCk8Vx1MDAxNmR6s0UlRzLNpeBLR0RV4v685oaD4jPY0NPsVebs7vJg57F01dk5XGbCp6fDx5RDVEg0ouhcdTAwMWNcdTAwMWSXiE87tPmJnlx1MDAxM5VcdTAwMTIqfVx1MDAxNFwi2juR3ox7J53xS2hE41x1MDAxZnV4d15q9G3KTlx1MDAwZtCNXHUwMDFkeLo536tnXHUwMDFmg+PcyupZ+bpTT3tIlFx1MDAwMEqln1x1MDAxZVxykKKakFx1MDAwMYBcIvs0jCuDRtZcdTAwMTht40l6aUOosPTH+OVcdTAwMTPzNpGIXHUwMDFhb7RA9j01Qlx1MDAxZreeosuTnZvsej5Y27292i1VXHUwMDBilylHaMYxjb5Ro7sw1jo/lMuMXHUwMDEwdVxmNH5xZHqopdJrRNH+S8f50mXhOZm4r1x1MDAwNFxcWSPFXGZcdTAwMTX0fv9O3EeZ7f3mw1G4tZepd831UdqjTcI7KqHn6OVcdTAwMTGqcdbzXHUwMDFhbJJMXHUwMDBi06u5621ipFx1MDAxN6FcdTAwMDLXXHUwMDEw18tcdTAwMTdcdTAwMTH1yVx1MDAxOSRegzbe++n9fL5WXHUwMDE1QSOzWatnczWT3d5q+KdfLOanSXKyXGZcdTAwMDFIVFx1MDAxNP29XHUwMDFm3lZCiCovgMAjadcmtVx1MDAxMLVWXHUwMDFhVHSwdEbUJ1x1MDAxYVGPQklRjcTUXGK9PD8uNfxx+eaidpI7M6b10lq9SLmfl5Z2NlGva42kXHUwMDE0yeZgxFx1MDAxZfCyRiGPcl9cdTAwMTmZZlx1MDAxYiqpwoxcYvOSITTeXHUwMDEyYKSAXHUwMDFlNS66PTu9n89d7ja3ajtbJ9nWzr45i04vzttp75SjlGEgONJQZKRcdTAwMWH8oFjyWjCgzFx1MDAxMqnI1ac4JCqUXHUwMDEy6Pbs4rqQ/PokPEfJkVx1MDAxNmZItverl/Xzx+p1VJSrsF15Ka5cdTAwMDdPXHUwMDBmKUeo8MBQyjvuvbFGXGJlhyCqXHUwMDE5uk+Jt4JcdTAwMGIlVXrlvMd15rhbOlwiXHUwMDFhN1x1MDAxYSNldVx1MDAxZeeNXHUwMDFimN6IXHUwMDFlXHUwMDFk7LlORl2Is+fb+pPblFFxO0g5RDVHeKBcdTAwMTDG+ZVcXOEkXHUwMDBmx0Q9s1x1MDAwNkU9UlRLe6PphaixWnprli2T2SeniaI6VJTEO71cdTAwMTHdKIb11sNhcVx1MDAxM8Tu6snd1t3TeniSeoSSm0cmylx1MDAxZJohwVx1MDAwN21oLyaKK1x1MDAxNc2r9lap9KaOXGKJy1x1MDAwN7XD0lx1MDAxOVGf3LJcdTAwMTGZl6e9lult6Lrbe8zXNtZfwpuwWatcdTAwMGLYutm6TzlC0Xoyjnpd4uyjXHUwMDFi5+CGIUo7o15cdGNwduI8L3VcdTAwMTjVVJ0q49ssy4FRpVx1MDAxMneWkHvR1OhcdTAwMTlcdTAwMDR99bGws7lcdTAwMGZh96q4vul2XHUwMDBicLJcdTAwMTckZThccoFtXHUwMDEwosPJmO+vKlx1MDAxN9rVYFx1MDAwNoy6XHUwMDBm4/YgKeAkjXZcdTAwMTR3XHUwMDFhjttTbbJcdTAwMTVUUumMkVx1MDAwM7tcdTAwMTifwGjUKtTbzUJcdTAwMGIxMIpTYzSz1oFxXHUwMDAyvDKxOv53nFwiQlmvo5QxSJ7FuJZjVGfmpJ5cdTAwMWZO3y70gVx1MDAxNZvvhrvaPrySK83z0/Z5pdbydZvtN+9cdTAwMWFAYaHVajz9fL/y15+T3jd7uX/LXHUwMDBiXHUwMDE39qB6WuJHzWJcdTAwMTTumZ3p3vftf8mri3J+wdiFrC5cdTAwMDEx2jhcdTAwMWOMXHUwMDEwTkol4onnXHUwMDFmNlC5rlx1MDAxNKKbo8PTRqaqiqXN09MtnVx1MDAxNIz43PL6jl5+2oIxXHUwMDE2NMo844fqXHUwMDAxXHUwMDE1MKWF8r1Cpngs6jNcdTAwMGWgUqmMriqlgVx1MDAxOUr+kqQzQcDoqkLPxDxVMqDYtFx1MDAxMsyYQISXVHNrXHUwMDE2tKouy+dr8qHycFA99NY2Vu1R9mwu6EfB4rXQseLgb+U/k5pcdTAwMDc5IHFcdTAwMGbTp3650vZevVSMoue9R7ArL+V93emmfTvDXHUwMDE4plx1MDAxNDWpRHBro61cdTAwMWIk6VZcdTAwMGKmXHUwMDFjte9G/jtQ0p02XHUwMDAy5ITSXFwv3Z6wh0SIWrxXg61cdTAwMTE/QujOSu7xKbzL6fzV/vrmak6vupNS2lx1MDAxMYpcdTAwMDaBSWucorZcdTAwMTZcXEg9XHUwMDAyUC6s5EhcdTAwMDSpnjvNXGadci+8WTqG7lVcIoUwXpLCXHUwMDE300NcdTAwMTTOt7Ln3Vx1MDAwN7e9una696gvQ7W2/YtbsE2zJyyZQ9vjkX57591wQ2AtmaJiQ/Dou9Pcm8XTXHUwMDBlPjqBZbOiXCLeXHUwMDAxd3hX2FxugPj1jyDafV65XHUwMDBiXHUwMDBi4cZu/qSbz5/7YrhcdTAwMGatz5Dc4Vxmq28luZzjPUd+6Vxy5XJcdTAwMGZ5eWS5XHUwMDBlXHUwMDA1JFx1MDAwMtRS8sJ3kFxcx4zGMShqXHUwMDAwZ/RcdTAwMTjpKIhpK4GKXHUwMDAzqEuMiFfqvKUsSOBK6Tnutk1cIrlSdp/32m1x+qTrYvf5aNef1trTkdyJ0vH7yDNJRyVNrFx1MDAwZtFcdTAwMTfXVFx1MDAxMIa1Znv8ilJcIrmBsfZcdTAwMWW4nyXRYi2z/bJzepBT3SPVvs03N59cdTAwMGXDpODhxDW1OKNvkXjQXHUwMDExXHUwMDFhhvqqxrtcdTAwMTa+mnyDtFx1MDAwNDVcdTAwMTlqNvfF4vJKweCkjq4pko24ZPH1XHUwMDFhmVH8sI73NVx1MDAwNehcdTAwMTOk69Vm9trVq+ElJbzUvb6h89t8+aY1NVx1MDAwNfa1NzG59p3YTz5cdTAwMDLBeGOUniVu/sxrV9XHXSfE01634nObnc6lTDf0XHUwMDFkmmoqSrdcYm2qtlx1MDAxOe4uj4SdWimCV4iuL+a6V1xuRXRcdTAwMWLfg37qbWnNPGORXHUwMDBiozuT4KllYqY7XG55ZOTxhidcdTAwMWZ2nO1cdTAwMWWdn1xc3rTr2Vb9zJrM/Y6BzXTD01uGXHUwMDA2U0nuPNdeXHUwMDBlgVMxwFx1MDAwNSxcdTAwMWMlwVx1MDAxOfe19klCXHUwMDE2XHUwMDAxxpDxeYBTalxyQs4zpJdcdTAwMGUu7pNcdTAwMGbnXHUwMDEwyminZypcYn7Y2axtXHUwMDEzcXBXL/XOirx7Upu/uMHXXHUwMDE0dNxcIlx1MDAwNr3qKUZj4seS9XaE6Fx1MDAxNC7KVUVcdTAwMTGHbi3F7ZOEkJb2epauf1K8XHUwMDFm0MimI46EXHUwMDBiN0MpxqZ7qDm/W2msXHUwMDFkrF1GK7X7YG/9XHUwMDE3N+2eLqbhlTGAupFcIr+DPp5601CNXHUwMDEw6jRKtUpzjiaOTjngv6OTn9xYPrlnN5F6QPc3Q1x1MDAxNvFxtqP2Xe5hnW+s7u095lZlYkFbSry8cVxmuSVaSXS0YOKHi/bS34xEI2pcdTAwMWR1i6QjN7+Ez1JQ1uXCKD5cdTAwMTUyXeW4clZah4slXHUwMDE29+uHNahdI3ikXHUwMDFiXHUwMDE0XjHW2mGE9qJcdTAwMWVOiflcdTAwMDXd3i6MlWB201x1MDAxY5xl1ctcdTAwMGVcbteN64xZ27o+OJ6LXHUwMDA0M1xcgOYm5ry/N3cpXHUwMDE5/lxiXHUwMDA2UGZcdTAwMTaSXHUwMDFiZFxu4uRcdTAwMTai5sWzWt3oVvaz66lnXHUwMDExyCCYk9bQ8Uh0XHUwMDA0iFx1MDAxY23NxLmlY+gsTotMb4c76KWU2KWz0GBcdTAwMTNTl0BcIren7phTQ/QqbK+oze76+Vx1MDAxNc9cdTAwMTVcdTAwMGWti1xumysq3Vx1MDAxNlx1MDAxYWUms1x1MDAxMkWWXHUwMDE2XHUwMDBlXHUwMDE1l4WhXGZlo5mnXHUwMDFlxpwyXHUwMDE34GtKTJdsUFx1MDAxOVx1MDAxMyZcdTAwMTDeM4WKV3hJPaL4mOxcbiFcdTAwMTlCV1rq5lx1MDAwNtrH2/C918rhJzBmXHUwMDAx5yTSXHUwMDFlJnWlXlx1MDAxMM2ViVJMO4nuk8/QXHUwMDBl/Oqhe3B8tVLYjfZW99TFXHJf21x1MDAxMOnfXFyWilFcIrKhPHmQhlx1MDAwZndcdEVcdTAwMDSDVkohkzDxM/vSZkNcdTAwMDXFOpxbvrZccrhukzCKRkNcdTAwMTL9miFBLVqrXHUwMDFkVGvtqlup6M1G5rbmzoNi2kGKPp6qjdCLK6c1QnFcdTAwMTiknkluXHUwMDExq1x1MDAxNC6K979LXHUwMDFiSHGmXHUwMDA0UCrrkmFcdTAwMTRsYlx1MDAwNoTstb6JXHUwMDE3t3yYorx+5neei2vXXHUwMDA1ffTSvCxcdTAwMDTHpeukQ7tT4uhcdTAwMTVYJiVyUaD84/jhur2Qq7Gokjxcblx1MDAxY8Wt4v5r+EzSYkIxTYmz1lCCpPWxvNa+p/eajlx1MDAxYXNcdTAwMDJcdTAwMTmJljYukPtp9I7aNsyx2fLbhbFibDtfs+FZ/SVqXHUwMDE3+NXa8cHGXfelMTcxZlFzLqqkObnfOKWpcWvMXGZUV2yWSt3cYe14k992/MvmmdxIOio0NStcdTAwMDCYsFJcdTAwMGKiu9xcdTAwMGI7SHUtSjXllPROOan813L0k7kudZMmmlx1MDAwYlxidCpcdTAwMDdcdTAwMTljo0FSM0pOJZK9ipZcdTAwMTGuS92mqX3F91fjXHUwMDEx15U4XHUwMDA2mFx1MDAxN0SjoFx1MDAxYo1Dp0guuLcoxJw0M+RQXHUwMDFll7NcdTAwMGaHNydmq3nz1L3PRrdB2z2nu4JEo1x1MDAwNqLKXHUwMDEwiSSCmrTYwexcdTAwMWanqdaZXHUwMDFiytn48knLY7N/pFx1MDAxOYNENVx1MDAxMlxiQ5uNLntRgbDvKlxyOd88tqVMqVx1MDAxMOZbqttdy+fyXHUwMDFkeTqnXHUwMDFjXHUwMDA3a4FcdTAwMWHMzbBg+sBs1KPT2kuPk8DAo5uF+1r4PFx1MDAwMK7eSsJcdTAwMDE+1lpRp1x1MDAxMF638YXsbeZi099cdTAwMGVwXHUwMDAwvXc0XHUwMDAzL11cdGs3tPB+hkFlcEVGNbwx75ejRmxcdTAwMWKkhEMp4Nu1dsrDXHUwMDFmqdGq3dTqhfBswrAmWobXXHUwMDFiPcY0qGRcdTAwMDVsUPRJZ2do8D5cdTAwMTlRvyAx8EPLgHqXIaq0RFx1MDAwNoe0x1x1MDAwZtU/OmVcdTAwMThcdTAwMWRcdTAwMTVcdTAwMDKoK5A1qa+FaMYnXHUwMDA2Mo5uXHUwMDExXHRcdTAwMTeKbE6pSGO2yqXWzDvjKTlQXHUwMDAzWDFiOchjOVx1MDAwN8BcdTAwMTdRQz57fVbstlx1MDAxNVrRaq1ertVv8GLfevxcZl5/9c5cdTAwMTQ+hj5cXKH5Snkpzi3wznhUO7FccqHeyi516INkOEPeTWeaeeWIXHUwMDFmg3x70rtcdTAwMTn7XHUwMDE51MtcdTAwMWZcdTAwMGZqcunk+6CUYF5cdTAwMGLA2TBC+T4vjFx1MDAwZkkwIZRGLWtcdTAwMTRSdcpcdTAwMTRcdTAwMWFcdTAwMTlSWGhHa437+1qEt/6oUatHw7e4dy9XaGFXg8KIycCPXHUwMDE0vzZsXHUwMDAxmvSOgy6i/79cdTAwMWb9VdL74f3///5z7LMzySDuXVx1MDAxZcVv/1x1MDAxZP+I/zuz+Vx1MDAxMsl7gJRXIES8n/xH5muy40qn+fJcZjWlprx1izLbXGbtgCAp59zRXqjzgsM3XHUwMDE073FmkDUhrzLcoHBcdTAwMTRuTGKz4pJJS6ebW0F9OmDUfFx0OqtcdTAwMWVF1OJaYMyySTd38/W3IeBcZsCTm/WANpUqmEfMXHQwXHUwMDE0j6jZXHUwMDA1XHUwMDAwiXbrJ1x1MDAxYq/BT/FcdTAwMWLZkGQg0dcohGa0IEnSKN7mdSRcdTAwMTlcdTAwMGIlXHUwMDFhmvJcdTAwMTm0USP3fHHvXHUwMDFhQbOUvWxcdTAwMWbqrjeZ809lXHUwMDExLE5cdTAwMWKBZ85cdTAwMWJUzdqgbOZ8MIuAKlx1MDAxZZmmykdcdTAwMTT4SukvxlbHiyM9lTiiNFtnYI7ng7xDaL717Vx1MDAxM8XR9Vx1MDAwM1L8+7Xyzfo1+svts6PDnVJ1buLIU6erXyOOXmczbdrodVSf41x1MDAxNtIkXHUwMDE3RyPRpqyq6bnFZDylkVsojayOTrKmPHdDnVx1MDAxMIYsg5RcZijjhfr/qa+eKzTWMlxiblx1MDAxOeCvRiaNXGLXmo8xXHUwMDE0glkklVx1MDAxNlx1MDAxZCN1/Fx1MDAwNFx1MDAxZlx1MDAwYp78XHUwMDFkVXHguFrEtuD3XHUwMDBio8lcdTAwMGXmnTR4Jr1z0jrR61bIx4pcdTAwMTDOOEXDKHeGU1xuKzrdydzii8JIXHUwMDAyXHUwMDEzkvJcdTAwMGLQzzhFp1x1MDAwMY2XRtxz2shQlFx1MDAxN2Hsby6NkiFMX5lR9M7Ia5KVkUtsVO1cZkphcGL6XHKJyV4rjdbLaUat/7g0mpuB3npvxssxXHUwMDA3XFxcdTAwMTmr8Vx1MDAwM1x088VcdTAwMDK1scbLoMpcdTAwMDdLMFx1MDAwNlxcYvFGXHUwMDFhcePluFx1MDAwNuetoj6VOJBcdTAwMTHjhbBcdTAwMTB+UUdIf5o/zMt8xYxcdTAwMTOdriyRmSrPgbpcdTAwMWXEYztv4Vx1MDAxZkOnNtGhTFx1MDAwMqRQ2n9gv35bbZRJxtLr5Vx1MDAxMVx1MDAxOM1oRVx1MDAxMtVRcvNccrQggJiZ4cyQo+rWUzfMZ5+fX9aMWslqU6+s/0oj4j/sPKaYRNugnNCSmuPBUCtxo/DOa6Q+YD1Y578hvjKtOOpcdTAwMWRY4+epjpJ3LSVcdTAwMTfcwSxF1l/TXHUwMDE5qCQqrF1qNcLwulGptINU7MGMXHUwMDE51ec8tUruQCY5rjCNXHUwMDBlavpChslcdTAwMTXGaXTVXHUwMDE5Z5hQRijK01x1MDAxNsrqwdRcdTAwMDHteW/z1oLUSGrENywyIVx1MDAwNdOW2kOgXHTt9VxmXHUwMDFl46qtYo6OonZcdTAwMGX9kVWjPcgknXfi9GKO5cH7IO3nNkKHfPWwV/vYi0/uVVx1MDAxMPfimjpagESqidxcdTAwMTZvT1xch7z7cOWpM1xmd8oohbrg96b7iUjqXVx1MDAxZMHQnPy0SDYhXHUwMDFhx6GUnKFcdTAwMTTq5OLusH7zeF677VxcZVx1MDAwYkadlcVumG4/LYVjSmo6hJvjXz90bpK1llx0R2RcdTAwMDX1p+P8+zqEitjin+SuUVx1MDAxMnrkXHUwMDBic0yof0fS4ttcdTAwMTXSWVSLI1x1MDAwM69cdTAwMWX3OVx1MDAxNVx1MDAxNODvsXzO8Us9oW+YXHUwMDAzXHUwMDA3Wk5cdTAwMWZgnDzHqVxctVS+JdA90KG64NRQ0ixIRl3ikVx1MDAwMCtJNuz71iyyeFx1MDAwZd6j4DRcdTAwMGVoK2SM93eU32tcdTAwMDVcdTAwMWRhKfGvXHUwMDFiSVx1MDAxYlx1MDAxNFx1MDAwZXmxp4rLXHUwMDA1MPAvLLoplfpkJ/AjvoUpPWhqXHUwMDBlj5ZXcydjXHUwMDFl729cdTAwMWavXHUwMDE53jlAfY5cdTAwMDJWXHUwMDE56z6ZgDG5ue7AmEBcboppXG5cXEdIy5xcdTAwMWRcdTAwMWSTY9yjx0D00amCyrrfm3ckYpi+MqPwnVx1MDAxM/GQMnn7VKBittrPcFxuiTzKtNbD41x1MDAxNd9ccnNbudr9bl5cdTAwMDCk24YpYPhcdLmm1sXUXHJg6JhcdTAwMWPnXHUwMDE4OXque7Hfr5X4TTZiMSoxkXj0XHUwMDBlRpvjbsgk3vG9pdZcdTAwMTStXFw47+imiHd0P8k7dKxX2/CilVx1MDAwNmVcdTAwMTh+n554TJ7kNC5aScFUw42WXHUwMDAyXHUwMDE1XHUwMDE5wNCapbPU0U2hwtbKf/HIi4lrVlx1MDAxOdpg7Vx1MDAxZIntpIiXXCL0eVx1MDAwN/pO6o/EXHTz1nM1skEgnCCnOs+k8Y9cdTAwMTbdd6ZOTXZcdTAwMDE/XHUwMDA2ty+N94pcdTAwMDKf6OC1pLlcdTAwMWFx857R0V9KoS9CTT66lThcdTAwMTXvmNxcdTAwMWIkNibqNyC5kFx1MDAwMII6p+uR4YxuUPxOJCNcdTAwMTGv9JVcdTAwMTmF6pw4hpqUosV7jVx1MDAxZvz0u1x1MDAxMFx1MDAxYnZrrds5z8NcdTAwMTU/vburVc15mEs3x5BcdTAwMWFcdTAwMThcdTAwMDWNXHUwMDE0cmjq56JcdTAwMDZcdTAwMTMxPFx1MDAxNaZSQqOToMxcdTAwMTerXHUwMDBiJ3NcZlx1MDAxOFx1MDAxM9yIn/D7VkAl8Ylaz/Fg3UlcdTAwMWPjOytcYmUvM2LxXHUwMDFj48d//V/9dXthcmaVXHUwMDFkfItF0Y5xw/tcdTAwMWNcdTAwMTORyUlcbsiqXHUwMDA1R188fWn7ZCSkcWXjckX14KhpunA8nqree7mhsjTtKDxuuVx1MDAxNMPFnHMkXCKWec1cdTAwMDWdr+SohL5/XHUwMDAz+kSE07neXG7QyjurjLCjlcOKXHUwMDFhTlx1MDAxMn1cXFx1MDAwMFx1MDAxMfnCypySiEz2XHUwMDEzQ0TEgpV0hCO3qPJcXL92+t3xO0b1XHUwMDFlkvZcdTAwMDU0UIX455jI5NL4wUFR0p7GgTlJJ7v50bCMYeg9fS/tg3tcdTAwMDD9e3OTJFxi01dmXHUwMDE0vUnc5I+33/Cz0GyeRoi599lANNfKbya9/zF/PtaCp9XxXHUwMDFijbTX+MfbXHIl81x1MDAxM9CH/c9ff/z1/1x1MDAxNXZx8iJ9
+ eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nN1da1NcIsuy/b5/xcTcj3fTp7Kyslx1MDAxZSfixFxy309866j3njCQh6AtIKCiJ/Z/v5nollx1MDAwNmxcdTAwMDRHnVx1MDAxZZiImZFcdTAwMDYsulZlrpWVmfWfP759+955aJa///Pb93K3WIhrpVbh/vuf8vxdudWuNep8Sfd+bjduW8XeK6udTrP9z3/847rQuip3mnGhWI7uau3bQtzu3JZqjajYuP5HrVO+bv+P/L1VuC7/q9m4LnVaUf+X5MqlWqfRevpd5bh8Xa532vzp/8s/f/v2n97fidG1ysVOoX5cdTAwMTGXe2/oXepcdTAwMGbQXHUwMDAyXGY/u9Wo91x1MDAwNkvgwVtcInx5Qa29yL+uUy7x1VxuXHUwMDBmudy/XCJPfd/frEC9fNndLc7nWlx1MDAwN+FylZpnK/3fWqnF8X7nIX66XHUwMDEzhWL1tpVcdTAwMThTu9NqXFyVf9RKnSpfh6HnX97XbvBN6L+r1bi9qNbL7fbAe1x1MDAxYc1CsdZ54OdQvTz5dFx1MDAwZv75rf9Ml3/KeVx1MDAxZJFcdTAwMDZcYoBeXHUwMDFi1C9cdTAwMTfl3cb7yJBzqFBrXHUwMDE1XGJwaFhcdTAwMGKNmCeCh/VfqvfoXHUwMDBm7LxQvLrg0dVL/ddo7c/Ltv+a++cva22kjHNktCFvVTAvr6iWa1x1MDAxN9VcdTAwMGW/xOnII1llrdLKe99cdTAwMWZnu9ybXHUwMDBmXHJoXGZfXHUwMDBm7uWK/PrmWqlcdTAwMDeNfyfvWL30fMfqt3HcXHUwMDFmsVxcWErAqf+e22ap8DTtYHmYxlrCYMLL9bhWv1x1MDAxYf64uFG86iOl9+xff75cdTAwMDOhzvs0hII1oFxirHJcdTAwMTNDdPGwcnXZndu6yeOP3f3Kwd3p4eJcXMYhSjZcdTAwMDIyzqLRXHUwMDBl+XZcdTAwMDQ7XG5SxEBcYoxTdGAzXHUwMDBiUlx1MDAxZb5cdEFZmjGMWqfTMFx1MDAxYZx2wTlcclx1MDAxM0O0fLC6znfv/Lxk9nbya35jY+fKZFx1MDAxY6I5wEhB8Fx1MDAxNMCx0/BcdTAwMWVcdTAwMDYxSlx1MDAxNiNjveJLoL1cdTAwMTlcdTAwMWVXdlx1MDAxMFxuhOj5XHUwMDBmzFx1MDAxYUJtSEMoKMO+g1x1MDAxZNzkjv54+3xr44T8eWNn97pwfLfK/Kb9ayFcbupNM2pcIkfMZ7S3XHUwMDEw2HO4XHUwMDAxhFpcZlx1MDAxMZOAXHUwMDEw2EIpMqQzXHUwMDBiUVxyzlx1MDAxOVx1MDAxZerMQdSnU1HNTs8nuc1bXGK9zp1cdTAwMTZwuTh3uH9avSovVneWVmLIulx1MDAxMfVcdTAwMTAxXHUwMDBiNVx1MDAxZa32vCpcdTAwMTNmsodQo1witqCGXGZcdTAwMThero4yi1BwPF3EPmDmuCilc1HPRoPvXHUwMDE3hIkxXHUwMDFhXFzBUty4vLwtXHUwMDFlwll8W2psu62MY1x1MDAxNDRFwTLpdsBcdTAwMTBcYlx0QfTk5ompKPNxr9EoTMx/5iDaey8wX/kqiJL6XHUwMDFhiDpMhSgp65yiKbho63G9OueWtlxiVlexnFt8XFzeqVx1MDAxZWXd0fPcsqdnXHUwMDBl5zTxv+RcdTAwMDdFPSM30p61vLXaXHUwMDA0rbNMRpVcdTAwMDfvlJo9kNpUkFx1MDAwNlx1MDAwYoaUm9yMzuF56Fxcrz36RnV1f+nwXHUwMDA3XHUwMDE0jupZl/S5XHUwMDEwKYW8XHUwMDFjeTGyx1x1MDAwZs5cdTAwMGZBlFwi1DxcdTAwMWLOXHUwMDEwi8fsXHUwMDAyXHUwMDE0nGVNz58zY1x1MDAwMLVOpVx1MDAwMVQrXHUwMDAw8F5pmlx1MDAxOKHzwTRCp3M1f96+0te2XHUwMDA07ZN6M+NcYoVAXHUwMDExXHUwMDE4XHUwMDA3XG5cZkqQTVx1MDAwZnp64y1jQ/E/ymr292CyXHUwMDBiUmT8eMfvnjGQslJIXHUwMDAzqbPoXWBNOzFGN+JudbGKp/mOKsdL9v4hbnZcdTAwMGYyjlFEjJhpKmJHrrzVw8F7hiibWFx1MDAwNoVTyMs2u3FRXmPMRlx1MDAxNOGXXHUwMDA1Rr/K0etUR1x1MDAxZjAwuVFqcojS1VJ8XqrcLe/fkSqdXHUwMDFlx2f1pTjjXHUwMDEw1dZEvFx1MDAxNHkxguH7rmhcdTAwMDCiZCHyXGZdoyUspTNsQ8k60Dxfs1x1MDAwN9DUyD1a8W9mXG5cdTAwMWK6cGVOOnBZujjcKraO7lt4WC1cdTAwMTSyrpbEiFx1MDAxYVx1MDAxM2xARlx1MDAwMIBcdTAwMWFkolx1MDAxMnRcdTAwMDJtvZO9UfA2w1x1MDAxMHWIIH/NXHUwMDFjRFx1MDAxM9ubw2JJ81CQXHUwMDE35lx1MDAxNEb08mR1fdNcdTAwMWRuz5lyQ69vXHUwMDFmXHUwMDFkNU9d1jHKojBicHi0gVx0KeJcYkh1xFqJXHSAZuWUYEWZwygvMs9E5es26b+KikK6oKdgXGZcdTAwMTjvJo866dU5t1x1MDAxN6/XTu9cdTAwMTaal1x1MDAxN9v6uqIuTzPu6I3CyGrWhSGYXHUwMDAwrNuHXHUwMDFjPdNcdTAwMDDN0FDog2Zbm12IWvLOOHKzZkY9pu6Aslx1MDAwYnTOoZ3cim51zu5q5qp71Jy/OZ3rPOT396pLv1x1MDAwMUJccoHkM1x1MDAxMftJjYOKnvldxLiV6fdK8k0yXHUwMDBiUUmFcVx1MDAwZczMIVSlclFAQ7Lpoibfoz/ZvF85r3Yr8a3/sVZcdTAwMGY/XHUwMDE2XHUwMDFh9e2TrDt6ZpqRdV6zUlx1MDAwMutJXHUwMDFiN4RRXHUwMDE3Ke15Zlx1MDAxODySSJRZjGpcdTAwMTWQzSg7v1x1MDAxOVx1MDAwM2lcdTAwMTKCw5FRXHRcdTAwMDeLkp3cjsYnj6fbtki71/a+3tneuL5sXHUwMDFk72bcjuY8S1wiq1x1MDAxMDyixGyGIFx1MDAxYVTkwLKh5eWqvcmuYDKEOlg0OGNcYvVk0lx1MDAxMFxuXjtngp5cImH04r64fFRrgz87fFivVY3dP136xXHRXHRSnVx1MDAxY+shZFLOXHUwMDE49MYn8fGEUWAmYFx1MDAwM09ccrDiT9iorGGUL3qPxs/cXHUwMDA2aDpGhdtYayffXSq3K6Z4cLBBXHUwMDFizYWmPc3fVpfOV7NuQ0FFzDSDt6BJXHUwMDEy7lx1MDAxMj5DPsAzeFhGsdhnyczYyS5cdTAwMTn11jtcdTAwMTVmjYo6n55FonlKmIPh5Ho+qLjauT1fceXNUk3lj4u3uc1Gxlx1MDAxMcomlFx1MDAwNaFcdTAwMGXEXFyUQnK3V95cdTAwMWWMioLRvFaNVpjhXHUwMDE0XHUwMDEy1vFMRXD24vaJcNJcYlxy1V4pq6cwoeeNKlxctNY3rlx1MDAxZud3ri/umuvbx0v5jFx1MDAwMzTnfOTYVbD/9sw02ZBcdTAwMGUhXHUwMDE0I9H43mlcbtbr7GaLasVkWmlQM0dE0yNOxoBiRVx1MDAwZpNDdD9/kju4Ot5auyue3K5tl+P7++27jENcdTAwMTS0i4zYSDDIgthcZvt4XHUwMDA2MLDSZyFigoPsZtw77Sh8iVx1MDAxMSVR0MaS+1x1MDAxYYD69LIlbXje0E7u5JfW/P3F0UY9f1fePZybPyid3dazXHUwMDFlXHUwMDEyXHUwMDE1gErpZ2BcdTAwMDOEUlx1MDAxMzJcdTAwMDBQZp9cdTAwMTQpJDayRMYmk/SyhlCw8ofC7Il5m0pEKZBcdTAwMDFm31x1MDAxMyP0buW+c7y3dpFfPC0vrF+erFx1MDAxN6uF44wjNMdcdTAwMTaUfSP7XG5P1rowlMvMXHUwMDEwdZE3/FDM9FhLZdeIXHUwMDAyX3BKzVxcXHUwMDE2ntOpXt4rtKRhilxu+rB5XHUwMDA1153c6mbzZide2cjVu3S2k/VoXHUwMDEzXHUwMDA0JyX0SmnNUPU4lMtcdTAwMWN0ZIB6NXe9TYzsXCJcdTAwMTR4XHIpM3tcdTAwMTHRkJ5BXHUwMDEyjDdcdTAwMTRCmNzPn9aqUG7klmv1/GGN8qsrjXD/i8X8JElONmJcdTAwMDBcdTAwMDZyXHUwMDEyUVxuw9tKXGZRXGbgXHUwMDA1PFp2bTJcdTAwMGJRazWxovMzZ0RDqlx1MDAxMVxyyiNKjcTEXGI9PtotNsJu6eJHbe/wgKj12Jr/kXE/r63sbLJeZ2XIXHUwMDEwtENcdTAwMTF7z5dcclx1MDAwYnmW+0g6yzZUS4WZXHUwMDEw5lx1MDAxOUNosiXASFx1MDAwMb1cbsRuz07u51x1MDAwZo/Xmyu1tZW9fGttk1x1MDAwZTr7P47aWe+Ug0iRXHUwMDA3xTSUXHUwMDE5qfFhUCxcdTAwMDVcdTAwMDORl8xcdTAwMTKN4uozXHUwMDFjXHUwMDEyXHUwMDA1RGC3Z7+uXHUwMDBiya9PwnOSXHUwMDFjaf1cdTAwMTTJ9mH+uH50Vz3rnOt5v1p5PF8s399kXHUwMDFjoVx1MDAxMHzEUt6pXHUwMDEwyFx1MDAxMlx1MDAwMNohiJqI3afmW6FcdTAwMDA1ZlfOXHUwMDA3XmdOuZkjokmjMVJWXHUwMDE3eN5cdTAwMTT5yY3oztaGu83hXHUwMDBmOHi4rN+7Zd05Xy1nXHUwMDFjokYxPFhcYvP8aoU8yYP7St6GyFx1MDAxMot6pqhW9kazXHUwMDBiUbJGh2SkcjYgXHUwMDFh0oP2rFx1MDAwZVGSeCc3okvncb11s32+7GF9fu9q5ep+Md7LPELFzTNcdTAwMTNVjs1cdTAwMTCoQVx1MDAxYtqLifJKZfNqgkXMbupcYmhePqxcdTAwMWRmzoiG9JaNzLxcdTAwMDKriCmI6KLbuDutLS0+xlx1MDAxN3GzVlx1MDAwN79ysXKdcYSy9YxcdTAwMTTrdc2zz25cXHk3XGZR2Vx1MDAxOVxyXGJEPDtJnpc5jFx1MDAxYalO1cltltnAKGLqzlx1MDAxMnMvmVx1MDAxYTOFoK/eXHUwMDE11pY3fdw9OV9cXHbrXHUwMDA1v7dRTstwXHUwMDFhXHUwMDAy2yBEh5MxX95VKrSr5Skw6t6M23stXHUwMDAxJ03GSdxpOG4vtclcdTAwMTakpNJcdTAwMTHpgV2Md2C00yrU281CizEwilNcIlx1MDAxM1nrPDnwXHUwMDAxKVHH/4JTRmjU6yhFxORcdTAwMTleazkmdWZOm4/D6fOFPrBcdTAwMTLz3XAnq9sneq55tN8+qtRaoW7z/eZdXHUwMDAzKCy0Wo377y9X/vpz3OfmjzcvVeGH3aruXHUwMDE31U7zvFx1MDAxM2/Q2mSf+/y/9NUlOb+e7JesLvBcdNo4XHUwMDFjjFx1MDAwMKc1QjLx/M1cdTAwMDYqZ5VC52Jne7+Rq+J5cXl/f8WkXHUwMDA1I963vD6jl5+xnsh6wzKPwlA9IPpcYlxyYOhcdTAwMTUyJWNR73FcdTAwMDCVSmV0VaHxXHUwMDExSfKXXHUwMDE2nenBj64q9kxRkEpcdTAwMDZcdTAwMTabVnt6JVx1MDAxMFx1MDAxMbTU3NJcdTAwMTetquPS0YK+qdxsVbeDtY15u5M/+Fx1MDAxMPSzYFx0XHUwMDA2TKI4+FP5z7jmQc6LuPeT59W44upGvXje6Txs3Hk791jaNLfdrG9nXHUwMDEwRYjSpJLBbchYN0jSrYFcYp2072b+O1DSnTVcdTAwMDLkXHUwMDAwjTIztydcdTAwMWN8KkQt36vB1ohvIXRt7vDuPr46NKcnm4vL84dm3u1cdTAwMTWzjlA2XGKRtuRQ2loo0GZcdTAwMDSgXG6sVkxcdTAwMDSlnjvLXGZdci9cdTAwMDLNXHUwMDFjQ1x1MDAwZphKIShoUfgwOUT90Ur+qHvjVudcdTAwMTf2N+7McYxcdTAwMGKrv7hcdTAwMDXbJHvCOnJse1x1MDAwMtPv4IJcdTAwMWJuXGJsdIRSbOhcdTAwMDP77iz3Zlx0soPPTmDWrCgkO+BcdTAwMGXvXG5b8D55/S2Idlx1MDAxZuau4kK8tH661z09PVxu5/Gmb72H5Fx1MDAwZWdYfSrJVYrvOfPLQJLLPeTlmeU6XHUwMDE2kFxmUCvJXHUwMDBin0FyXUSGx4DSXHUwMDAwjswr0lx1MDAxMYRpI7Di8NIlXHUwMDA2kpU6zylcdTAwMGLaK0Tzgbtt40iu1t2HjXZcdTAwMWL2701cdTAwMWTWXHUwMDFmdtbDfq09XHUwMDE5yVx1MDAxZCtcdTAwMWQ/jzyLdERNiT5EP7mmynFcXGu2X19RXGLpXHKMTVxiXoVpXHUwMDEyLVx1MDAxNnKrj2v7W4fY3cH25Wlz+X47Tlx1MDAwYlx1MDAxZY5dU19n9C1cdTAwMTNcdTAwMGY5QoOkr2qya+GTySemJazJWLO5nywur1x1MDAxNIgndXRNiWzkJcvvN8yMkod1vKwpzz5Bu15tZq9dPVx1MDAwZS8pXGLa9PqGftzmyyetqVx0sG9cdTAwMDIl5NpnYj/9XGJcdTAwMDRcbkRopombP6jaSfVu3Vx1MDAwMdxvdCvhcPn29lhnXHUwMDFi+o5NtVx1MDAxNKVbhrZU21xmd5dnwi6tXHUwMDE0fUBG10/muldcbufsNj5cdTAwMDf90tvS0kfGXCK/jO6Mg6fRqZnuLOSZkSdcdTAwMWKevNlxtrtztHd80a7nW/VcdTAwMDNLues18svZhmewXHUwMDExXHUwMDFiTNTKXHUwMDA1ZYJcdTAwMWVcdTAwMDInRp5cdTAwMTcwOEmCI/dz7ZNAn3v/XG5cdTAwMTn/XGJwamM86I9cZullg4uH9MM5XHUwMDAwyTgzVUHwzdpybVWIgzt5rN/O6at7XFz+xVxyviag45YxXHUwMDE4sKdcdTAwMTiJkseS9XaE5Fx1MDAxNC7JVWVcdTAwMTHHbi3D7ZNcdTAwMDC0lb2emeuflOxcdTAwMDc0sunII1HgpijFWHY3NVx1MDAxN9YrjYWthePOXFzturyx+Iubdk9cdTAwMTbTXGJI5Fk3SuR30MdLb1x1MDAxYalcdTAwMTFinSapVlnO0eTRofPqd3Ty41x1MDAxYsun9+xcdTAwMTZS79n9TZFFvJu/xU13eLOoluY3Nu5cdTAwMGXndWpBW0a8PLmIuSVbSXa0npKHi/bS30izXHUwMDExtU66RcqRmz+Fz2K5ZEqFUXxcIjNddFxundXW8WJJxP36YVxyadfoXHUwMDAz01xyXHSvkLV2XHUwMDE4ob2oh0P4uKDb84VXJZhdpq2DPD6usXBdOsvRwsrZ1u6HSDBS4I2ihPP+3NyldPgzXHUwMDE4PNI0JLecK8Depe80fzzg/FK3splfzDyLYFx1MDAwNlx1MDAxMTltSY5HkiNA9GhrJqWsXHUwMDFjQ2d5WnR2O9z5XkqJnTlcdTAwMGLtbWrqktfM7aU75sRcdTAwMTA9idtzuNxdPDpRh4Vt6zqF5TnMtoVmmVx1MDAxOVnNXCLLgGPFZf1QhjKZKEhcdTAwMGZjJZlcdTAwMGL+55SYKdpy5ZUwXHUwMDAxhFx1MDAxMCErXlxiWnpEqVeyK0BHXGZdbaWbmzch2YbvpVaOv1x1MDAwMdFcdTAwMTeckyh7mNKV+otork6VYsZpdp9qinbgJzfdrd2TucJ6Z2N+XHUwMDAzf1xcqIUlyP7mssZIXHUwMDEykUny5L0mNdwllFx1MDAxMexcclwiMpOg5Jl9WbOhILFcdTAwMGXnZq9tXHUwMDAzr9s0jLLR0EK/pkhQ6yzUtqq1dtXNVcxyI3dZc0fl86yDlH28VFx1MDAxYrFcdTAwMTdHZ1xmQ3FcdTAwMTikIdLKMlYlXFyU7H+XNZDyTIGXVNZcdTAwMTnDqLepXHUwMDE5XHUwMDEwutf6Jlnc8maK8uJBWHs4XzgrmJ3H5nGhvFs8Szu0OyOOXHUwMDFlvY20Zi7qJf84ebhuL+RKllVSYIGDyqJcbj+HzzQtXHUwMDA2XHUwMDE4XHUwMDE5SZy1JFx0kjYk8lr7nj5cdTAwMTg5asyB9JHSNimQ+2n0Tto2fGCz5edcdTAwMGKvirHV05qNXHUwMDBm6o+ddkGdLOxuLV11XHUwMDFmXHUwMDFiXHUwMDFmJsYsa86vKmlO7zcuaWrKXHUwMDEyTUF1YblY7Fx1MDAxZW7XdpfV5W14XFw+0EtpR4VmZlx1MDAwNfhcYqw2IHRXXHUwMDA1sINU17JUQ4c6OHRcdTAwMWHDz+Xop3Nd6SYtNNcz0KVcdTAwMWPkXHUwMDE1XHUwMDFi7bU0o1RSXCLZq2hcdTAwMTnhutJtWtpXfH41nnBdzWPwXHUwMDFmXHUwMDA10U6523lccp2AqfthXHUwMDE2jSfvp+jivFvK32xf7NFK8+K+e53vXFyW2+4h21x1MDAxNSSGNZBUhmgmXHUwMDEx0qTFXHUwMDBlZv8443vZa1oh00c95qjlYtBcdTAwMDVdXHUwMDE4XHUwMDBmzlfTfzS9XHUwMDAyRVx1MDAxY4mEsdFmn/1VkbDPqlxyOVretcVcXLFcdTAwMTCftrDbXTg9PL3V+1x1MDAxZmPUwTnJ0ZsmO76PzEa9s1977JFcdTAwMTI/8Oxy4bpcdTAwMTY/XGagq7eUeIB3tVbntlx1MDAxMJ+1+Y3R88wlpr9d5lx1MDAwMfQ+kVx1MDAwNt46XHUwMDE31y5k5X2Py5XBJdmp8Y15udxpJPZBijyUXHUwMDAyf1xca600/JVcdTAwMWGt2kWtXohcdTAwMGbGXGZrrGl4utGv2Fx1MDAwNkxEzUZavKtARlx1MDAxMuYmNlx1MDAwZeMh9Vx1MDAwYlJcdTAwMDPftFxyrHgjJsug5HBoo0lcdTAwMGZpXHUwMDBipFxiia2D6Vx1MDAxNVx1MDAwN2D6Ps+7bVx1MDAwM0aKjY72Qlx1MDAxMVx1MDAxZDP2V1JcdTAwMDO1ZFNcdTAwMTkjXHQnhjTzs4RcdTAwMGV/5m1eNtNcdTAwMDdcZvknNtZkXHUwMDFmbmCaXHUwMDAyrcRdK7Q687V6qVa/4It96/G9/PSr1yZwMvLlXG7NJ84rgW6ggIHlTmJHqLeyi7fyRXIqYuIth5rx/Vx1MDAxNYLs9fOLXszY93K99PagxtdOvlxmXG4hXG5cdTAwMDY8z1x1MDAwNlx1MDAwMYY+MUxcdTAwMGVcdFwiXHUwMDAwNCxmXHSZq0uq0MiQ4kK7s9C4vq51+NbvNGr1zvAt7t3LOVnY1XJhxGTwV0peXHUwMDFitlx1MDAwME35xEFcdTAwMTfR/9+3/lwi6f3w8v9///nqq3OpXHUwMDE47l1cdTAwMWSFb/9cdTAwMDP/SP47tfWChMBcdTAwMWSOMFtWOuSmqF9cdTAwMWHvtzJpvEBH3rDMYTNtTbJT0NNcdTAwMDZcYi9cdTAwMGZcdFlIK1x1MDAxMGV9+lx1MDAxZeD7eU1cdTAwMTQoKC9dNlx1MDAxOMc6XHUwMDExIO7brmCjoK2R9sRcdTAwMDEg8ZKXJFxuOaieXHUwMDE11Fx1MDAxN5kuyUv6labrbyOgXCLvXHUwMDAzotXBs26X8uVcdTAwMTFT4iNWjjx34L0odlx1MDAxYsZcdTAwMWKuwW/xO9mPVFx1MDAxY8ljXHUwMDA0QVNajzRZpCn9ZFx1MDAwNulcdTAwMTQ71WGgjcOHXHUwMDFm165Rblx1MDAxNvPH7W3TXHKUO3pXXHUwMDAywdfJXCJcdTAwMWZcIr7dbLVccitAlviDXHRcdTAwMDRS7Fx1MDAxOFx1MDAxOSl6ZG2PaMaEVd9vPsxEskgybFx1MDAxZPlcdTAwMGY8XHUwMDFh5Fx1MDAwNUFcdTAwMWZb2j5WXHUwMDE2nd0wub9eKF0snrHMXFw92NleK1Y/TFx1MDAxNiEkj/b5Uln0NJtZU0VPo3pcdTAwMWat0JTa5Vx1MDAxNcBrfkyTWzRcdTAwMWVQWeRcdTAwMTVomNDJKdaS407SXHUwMDA1Ycg0aOZcdTAwMWSS7Vwivf9w3JlC7zZccqBs5PlXM4lmiFx1MDAxYlx1MDAxNmCjllx1MDAwMlwiy0KEiU+Qbp8+JOImf1x1MDAwN1RcdTAwMWPLOvz8LcHeXG7kXHUwMDFi9pnEYryHeeFcZiHSgVmOddDrVKhe1Vx1MDAxZipSXHUwMDEyXHSTvFx1MDAxOSXpq1x1MDAwNv92qp+jibSP2JtKfYpcdTAwMDe+UaTptVFcdTAwMDGDTskmXHUwMDA2Sk5cdTAwMDTZ31xcXHUwMDE1pUNYXHUwMDFluVH0Tslr0lWRT1VFYCE4YG00+W7EeL+VRfPlMFx1MDAxMpWvjFx1MDAxM3R7P3hYXG7IUVx1MDAxNc4rJGv4XHUwMDBiXHUwMDAxpSeGvd96XHUwMDExK3xvXHUwMDA1x57XWLKLRtJ6OWW8XHUwMDBiXHUwMDE2pUklJk6w+dt6MS4gfMX50T37ReinOSzlw+1XwjpcdTAwMDFTUtb2pEVcIklzvFx1MDAxMYtcIr3ztDNcdTAwMWGZXHUwMDE4PjWOXHUwMDE5b8B+X22UjqWnyyMwmtKMpMqj9Fx1MDAwZaPA9EeiXG6Tx1Z2qiv33fg0//DwuEA4lzdUryz+SiNcdTAwMTLebDuGkWbbgFx1MDAwZYyWznh+qI84Id95w9zH2+CtXHUwMDBi6Y03Pl1cdTAwMWX1TqtcdFx1MDAxZqmPxm7AiDv7MqXBWqJcdTAwMTK1i61GXHUwMDFjnzUqlXY5XHUwMDEz+y+vjOp9rlx1MDAxYdPPk5De4OzFpuhcdTAwMWQyvro4i5465yhcdTAwMDIkQMnRXHUwMDA2tGYwbcBcdTAwMDTV27jl22B6yPtcdTAwMDRPrSEyVlpDsFx1MDAwNe31XHUwMDBifsVTW4ycXHUwMDFjQ+2c8sriaP8xLWedOPNcdTAwMTVH8vTKjtFOVWWQ5qqHndrbTnx8n4KkXHUwMDEzN9LNwmtcdTAwMDfA3JZvT1KHvLhwXGbSXHUwMDE1RrxcdFwi64Lfm+6nXCKpd3VcdTAwMDRDXHUwMDFm5aZNelx1MDAxNFODYoJcdTAwMTTc5CZk78fVdv3i7qh2eXuSL1x1MDAxMFx1MDAxZZRgPc62n2ZcdTAwMDSxbpRjXGKIv21wg7FcbsZfXHUwMDA0ipRccrw+dXr+8lx1MDAwNFx1MDAwNmRsb9BkXHUwMDFisnG+mlx1MDAwNWFgsvCBqfQvOPraRoVshuQgx2mSl36OXHQ8fPvXt2eX+5BcdTAwMDVcdTAwMGUwMJ73eX9Krz+wcniNXHUwMDA1mGJcdTAwMDdi7FxcZ3LpgjKR8axt2KH1jl1cdTAwMWNO7MaIlKxr1oCa1d7nrV45csVcdTAwMWE54JeRbYx+Ra9cdTAwMWJcdTAwMTVZXHUwMDFlJZCXhlnJY5ZeSlx1MDAxMFTvaFb7JVx1MDAxYpk2KLTTdDpKI1x1MDAwMWmufrwr+JbcyNTBXHUwMDFi6Vx1MDAwZq/43imnXHUwMDEzju9vV29cIkmxMUH6t1wiWffOXHUwMDE0jPH9dVx1MDAwN8bEzFlCm0DBSSTIjo7JsdvQRlx1MDAwZXSWg1x1MDAwNdG635t+pEJYXHUwMDFluVx1MDAxMfR+XHUwMDEw/TDpLWQtXHUwMDBmiW/uXHUwMDE0UVx1MDAwMr2Tay3Gu3OhXHUwMDFiXHUwMDFmrlx1MDAxY9au1095rNk2YY6RJlx1MDAxOS1cdTAwMWV9YJpLQ+fkWFx1MDAxMzHzYzdpJfhtf2pcdTAwMGZ1vFx1MDAwNXP97zyWf/RORvvALZFx9ONcdTAwMTNrrcVcdTAwMDBcdTAwMDZy7zKA76JcdTAwMWbdvrvvZoF+XGaM5330I6TnfjJVZuOYrGR5a+2On+ssrl1cYkI+iFx1MDAwNVx1MDAxMlx1MDAxODaINFx1MDAxON9jK1x1MDAxYSlWrsRY41x1MDAxN4w5KLDE2kNV3r900UYuKM1SmNiDUnilWVx1MDAxMig2MyCWm6S1XHUwMDA1hNFcdTAwMDRQXHUwMDA34lk/Mnd83Nqz7FPDe9behORjvCf4NriVSSGgxEDZXHUwMDE3XHUwMDE5VoB21NeHSI5cdTAwMDBDXHUwMDA0lDzEN7ZcbtLGNL5HSGJM0ndAK9Deg3RQNyPDXHUwMDE53Wz9nZhGKl7lkVx1MDAxYoXqXHUwMDA3MVxyXHUwMDFjc+KZQUd2mkT1Jbuy0L09OvUnav/qqlalo/gw20xDo5aTRcBcdTAwMDVpyy9lVkNMQzNcclx1MDAwN81UXFzOQlCfp5X0a1x1MDAxOepuJK2TSbXnJfmB5+uOY1x1MDAxYZ9YSFxiTkqs35fa8dNM49t//1/9abNhfKaVXHUwMDFk/JivJFx1MDAxZq9ccvF9fFx1MDAwNCmVj0ibNctcdTAwMTR7cj4yXHUwMDFlXHUwMDExWVxc4dJNSTn+jqC0XHUwMDFmboondFx1MDAwNMiSXCKjpXz3p5JcdTAwMTbGrm+j5UBcdTAwMDGRiprFvE+e+fKy3IOPWGqCNEsnSc1cdTAwMWStI0ZpP0lIX5J7xVx1MDAxYd6bz6xHXHUwMDE57y6G6Ij1VsuBjspcdTAwMWG+gf1K6lx1MDAxN/fvXCIp/tCyU8BcdTAwMDTUvZXWnZp7NbZQfnBQksZneGBOyzlvYTRCQ1x1MDAxMSrpt2htUMF781szlHRcYssjN4LeNIbyx/Nv+F5oNvc7jLmX2WA010rPlr3/Nb/f1cr3869vPcru41x1MDAxZs83VKxPWb7sf/7646//XHUwMDA3cfF+/CJ9
- virtual_size.height virtual_size.width self.scroll_offset scroll_y scroll_x scroll_x + self.size.width
+ virtual_size.height virtual_size.width self.scroll_offset y = scroll_y x = scroll_x x = scroll_x + self.size.width