# Rotation — the URL is the API

This page keeps the whole lineup in the URL fragment (the part after `#`).
Nothing lives on a server, so you don't need this site, a browser, or any API
to read or write a lineup. If you can build JSON and base64, you can set a
lineup and hand the coach a link.

## Links

```
https://tools.marplecoaching.com/rotation/#r=<payload>   editable
https://tools.marplecoaching.com/rotation/#v=<payload>   read-only view
https://tools.marplecoaching.com/rotation/#g=<payload>   read-only, groups only — no minutes
```

`#g=` is the one to send to players: who's in goal each half and who rotates
with whom, with the minutes left out. The same payload works in all three.

`payload` is base64url of UTF-8 JSON: standard base64 with `+` → `-`,
`/` → `_`, trailing `=` stripped. Browsers never send the fragment with a
request, so names stay out of server logs — but anyone holding a link can
decode it, so treat a link like the roster it contains.

## The JSON

```json
{
  "game": 60,
  "slots": { "GK": 1, "CB": 2, "FB": 2, "WB": 0, "DM": 1, "CM": 0, "AM": 2, "W": 0, "ST": 1 },
  "groups": [["CB", "FB"], ["DM", "AM"], ["ST"]],
  "k1": 8,
  "k2": 9,
  "players": [
    { "n": "Ada",   "p": ["CB"],        "g1": null, "g2": null, "out": false },
    { "n": "Bex",   "p": ["FB", "CB"],  "g1": null, "g2": null, "out": false }
  ]
}
```

- **game** — whole-game minutes, 10–120.
- **slots** — how many of each position are on the field at once. Codes, back
  to front: `GK` keeper, `CB` centerback, `FB` fullback, `WB` wingback (the
  wide player in a back three), `DM` defensive mid, `CM` central / box-to-box
  mid (the 8s), `AM` attacking mid, `W` winger, `ST` striker. `GK` is 0 or 1
  (3v3 and 4v4 play without one), the rest 0–4. A code you leave out is 0. A 9v9 4-3-1 is the block above; the
  page's presets, for reference:

  | | GK | CB | FB | WB | DM | CM | AM | W | ST |
  |---|---|---|---|---|---|---|---|---|---|
  | 7v7 2-3-1 | 1 | 2 | | | 1 | | 2 | | 1 |
  | 9v9 4-3-1 | 1 | 2 | 2 | | 1 | | 2 | | 1 |
  | 9v9 3-2-3 | 1 | 3 | | | 2 | | | 2 | 1 |
  | 11v11 4-3-3 | 1 | 2 | 2 | | 1 | | 2 | 2 | 1 |
  | 11v11 4-4-2 diamond | 1 | 2 | 2 | | 1 | 2 | 1 | | 2 |
  | 11v11 4-2-3-1 | 1 | 2 | 2 | | 2 | | 1 | 2 | 1 |
  | 11v11 3-5-2 | 1 | 3 | | 2 | 1 | 2 | | | 2 |
  | 11v11 3-4-3 | 1 | 3 | | 2 | | 2 | | 2 | 1 |

  Anything else is a custom formation: set the counts you want. A saved team
  (`/team/agent.md`) carries the same `slots` object as its default formation;
  a lineup opened from a team starts in it.
- **groups** — the rotation groups: arrays of position codes. Together they
  must cover every field position that has spots, each position in exactly one
  group. Any mix is legal — `["FB", "W"]` is a fine group. A position left out
  of every group is auto-adopted into a group holding one of its linemates;
  don't rely on that, write it explicitly. The page keeps groups in pitch
  order, back to front by the deepest position each holds, and moves the
  per-player overrides along with them — so write them in any order, but
  expect them back sorted.
- **players** — up to 40. Per player:
  - `n` — name, ≤30 characters.
  - `p` — positions they can play, best first. The first one with spots in
    this formation picks their default group. `GK` anywhere in the list makes
    them a keeper candidate. The first two are her main and backup spots: put
    her in a group holding neither and the page marks it as a new role for
    the day ("usually CB").
  - `g1`, `g2` — group override for the 1st / 2nd half: an index into
    `groups`, or `null` for automatic.
  - `out` — `true` means absent today; they're ignored everywhere.
  - `lock` — `true` means the coach has decided on her: the page draws her
    chip solid, won't drag it, and counts the rest as "still to place". When
    the page locks a player it first writes her current placement into
    `g1`/`g2` (or `k1`/`k2`) so a regroup can't move her; do the same when you
    set it. Leave it out when it's off.
- **k1**, **k2** — who's in goal each half: an index into `players`, or
  `null` for automatic (the first / second available player listing `GK`).
  The same index in both means one keeper all game. A keeper's off-goal half
  is played with their normal group like anyone else.

## The math the page will show

Per half (`time = game ÷ 2`), then summed for the day:

- group minutes each = `time × spots ÷ players in the group`, capped at `time`
- a keeper's goal half counts as full minutes
- even share = `time × total field spots ÷ total field players` — the
  benchmark; the page flags groups more than ~5 min off it and suggests
  movers who list a position in a starved group
- `players − spots` = how many of that group are off at any moment (at 4+ the
  page says to swap in waves of 2–3)

Run these yourself before answering: aim every group near the even share
unless the coach asked for something else, and call out any group with
players = spots — those play every minute.

## Worked example

12 players (one out), 9v9 4-3-1, 60 minutes, keepers Posy and Quinn splitting
halves — both list `ST`, so each plays their field half as a forward:

```
https://tools.marplecoaching.com/rotation/#r=eyJnYW1lIjo2MCwic2xvdHMiOnsiR0siOjEsIkNCIjoyLCJGQiI6MiwiRE0iOjEsIkFNIjoyLCJXIjowLCJTVCI6MX0sImdyb3VwcyI6W1siQ0IiLCJGQiJdLFsiRE0iLCJBTSJdLFsiU1QiXV0sImsxIjo4LCJrMiI6OSwicGxheWVycyI6W3sibiI6IkFkYSIsInAiOlsiQ0IiXSwiZzEiOm51bGwsImcyIjpudWxsLCJvdXQiOmZhbHNlfSx7Im4iOiJCZXgiLCJwIjpbIkZCIiwiQ0IiXSwiZzEiOm51bGwsImcyIjpudWxsLCJvdXQiOmZhbHNlfSx7Im4iOiJDbGVvIiwicCI6WyJGQiJdLCJnMSI6bnVsbCwiZzIiOm51bGwsIm91dCI6ZmFsc2V9LHsibiI6IkRvdCIsInAiOlsiQ0IiLCJETSJdLCJnMSI6bnVsbCwiZzIiOm51bGwsIm91dCI6ZmFsc2V9LHsibiI6IkZlcm4iLCJwIjpbIkZCIl0sImcxIjpudWxsLCJnMiI6bnVsbCwib3V0IjpmYWxzZX0seyJuIjoiSWRhIiwicCI6WyJETSIsIkFNIl0sImcxIjpudWxsLCJnMiI6bnVsbCwib3V0IjpmYWxzZX0seyJuIjoiSnVubyIsInAiOlsiQU0iXSwiZzEiOm51bGwsImcyIjpudWxsLCJvdXQiOmZhbHNlfSx7Im4iOiJLaXQiLCJwIjpbIkFNIiwiU1QiXSwiZzEiOm51bGwsImcyIjpudWxsLCJvdXQiOmZhbHNlfSx7Im4iOiJQb3N5IiwicCI6WyJHSyIsIlNUIl0sImcxIjpudWxsLCJnMiI6bnVsbCwib3V0IjpmYWxzZX0seyJuIjoiUXVpbm4iLCJwIjpbIkdLIiwiU1QiXSwiZzEiOm51bGwsImcyIjpudWxsLCJvdXQiOmZhbHNlfSx7Im4iOiJSYWUiLCJwIjpbIlNUIiwiQU0iXSwiZzEiOm51bGwsImcyIjpudWxsLCJvdXQiOmZhbHNlfSx7Im4iOiJUZXNzIiwicCI6WyJDQiJdLCJnMSI6bnVsbCwiZzIiOm51bGwsIm91dCI6dHJ1ZX1dfQ
```

What that decodes to, per half: Defenders (Ada, Bex, Cleo, Dot, Fern) 5 for
4 spots → 24 min each; Midfield (Ida, Juno, Kit) 3 for 3 → 30, no rotation;
Forwards 2 for 1 → 15 each half. Even share 24. Day totals: defenders 48,
midfielders 60, Rae 30, each keeper 45 (30 in goal + 15 at ST), Tess out.

## Recipe for setting a lineup

1. Decode the coach's link if you got one; otherwise start from the schema.
2. Edit: availability (`out`), positions, groups, keepers, per-half overrides.
3. Do the math above and sanity-check the plan before showing it.
4. Encode and reply with the `#r=` link — plus a `#v=` link if it's going on
   to an assistant coach, or `#g=` if it's going to the players (groups, no
   minutes).

Bad values never error — they're clamped or fall back to automatic, so a
sloppy payload still loads, just maybe not as you intended. Decode your own
link once to confirm it says what you meant.
