#!/usr/bin/env python3
# ---------------------------------------------------------------------------
# claims-options.html — layout options for `.claims`, the thirteen injury
# claims on injuries-index.html.
#
# Adi 2026-08-31: "buat beberapa opsi tampilan layout untuk class=claims".
#
# Every option carries the real thirteen claims, their real URLs, and the
# photograph the LIVE claim page uses for each — all EXECUTED out of
# gen_injuries_index.py, so no option can drift from the page.
#
# What varies is the layout. What does NOT vary, because it is settled and
# measured, is the tone (all thirteen greyscale — eleven already are) and the
# colour (brown reads, orange marks).
# ---------------------------------------------------------------------------
import io, os, re, sys
from PIL import Image

OUT  = "/srv/apps/leaguelaw/wp-content/prototype/claims-options.html"
IGEN = "/srv/apps/leaguelaw/prototyping/gen_injuries_index.py"

_src = io.open(IGEN, encoding="utf-8").read()
NS = {"__name__": "gen_injuries_index", "__file__": IGEN}
exec(compile(_src, IGEN, "exec"), NS)
GROUPS, PHOTO, CROP, UPLOADS = NS["GROUPS"], NS["CLAIM_PHOTO"], NS["CROP"], NS["UPLOADS"]
CLAIMS = [(t, h) for _g, _r, items in GROUPS for t, h in items]
assert len(CLAIMS) == 13
SIZE = {t: Image.open(os.path.join(UPLOADS, PHOTO[t])).size for t, _ in CLAIMS}
COLOUR_SRC = set(NS["COLOUR"])
assert COLOUR_SRC == {"Cyclist Accident Claims", "Pedestrian Injury Claims"}

# Measured in headless Chrome at 1440. Re-measure if the CSS changes.
# the layout's own height: the stage measured in Chrome minus the sheet's
# own 44px top and bottom padding.
HEIGHTS = {"1": 671, "2": 722, "3": 1088, "4": 1314, "5": 1018, "6": 945,
           "7": 1096, "7a": 1179, "7b": 1096,
           "8": 640, "9": 559, "10": 776, "11": 223, "12": 844,
           "13": 840, "14": 833, "15": 420, "16": 4224, "17": 362,
           "Ha": 0, "Hb": 0, "Hc": 0}   # 0 = not a height question


def img(t, extra=""):
    cls = (" " + CROP[t]) if t in CROP else ""
    return '<span class="ph%s%s"><img src="/wp-content/uploads/%s" alt=""></span>' % (
        cls, extra, PHOTO[t])


C9_HOVER = "\n".join(
    ".c9 .it:nth-child(%d):hover ~ .pv:nth-child(%d){opacity:1}" % (i, i + 13)
    for i in range(1, 14))

CSS = r"""
:root{--orange:#EF6D3E;--teal:#46ADC8;--brown:#423C37;--line:#E4E1DC;--paper:#F9F9F9;
      --muted:#6B6B6B;--deepteal:#2F7186;--deepor:#A94A29}
*{box-sizing:border-box}
body{margin:0;background:#EDEAE4;font-family:"Open Sans",Arial,sans-serif;color:var(--brown)}
a{text-decoration:none}
h1,h2,h3{margin:0}
.page-head{max-width:1360px;margin:0 auto;padding:44px 40px 10px}
.page-head h1{font-family:Raleway,sans-serif;font-weight:300;font-size:34px;letter-spacing:.06em;
              text-transform:uppercase;margin-bottom:14px}
.page-head p,.box{max-width:1010px}
.page-head p{font-size:15px;line-height:26px;color:#4A443E;margin:0 0 16px}
.box{background:#fff;border-left:4px solid var(--teal);padding:16px 20px;font-size:14px;
     line-height:24px;color:#4A443E;margin:0 0 16px}
.box.warn{border-left-color:var(--orange)}
.box code{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:13px;background:#F3F0EB;padding:1px 5px}
table{border-collapse:collapse;width:100%;margin:12px 0 4px;font-size:13.5px}
th,td{text-align:left;padding:7px 10px;border-bottom:1px solid var(--line)}
th{font-family:Raleway,sans-serif;font-weight:700;font-size:11px;letter-spacing:.14em;
   text-transform:uppercase;color:var(--muted)}
.frame{max-width:1360px;margin:0 auto 10px;padding:0 40px}
.oh{display:flex;align-items:baseline;gap:14px;padding:30px 0 6px}
.oh .tag{font-family:Raleway,sans-serif;font-weight:800;font-size:22px;letter-spacing:.06em}
.oh .nm{font-family:Raleway,sans-serif;font-weight:600;font-size:14px;letter-spacing:.1em;
        text-transform:uppercase;color:var(--deepteal)}
.oh .h{margin-left:auto;font-size:12.5px;color:var(--muted)}
.on{font-size:13.5px;line-height:23px;color:#4A443E;margin:0 0 12px;max-width:1010px}
.on b{color:var(--brown)}
.stage{background:#fff;overflow-x:auto}
.stage > .s{width:1280px;margin:0 auto;padding:44px 0}

/* ---------- what every option shares -------------------------------------
   Eleven of the thirteen photographs are already black and white on the live
   site — measured, not read off their filenames — so all thirteen are
   filtered to match rather than two of them reading as mistakes. The two
   portrait sources are anchored where their subject actually is: the
   motorcycle photograph is a rider's-eye view with the machine in the LOWER
   half, and the dog's jaw centres slightly high. */
.ph{display:block;overflow:hidden;position:relative;transition:box-shadow .18s ease}
.ph img{width:100%;height:100%;object-fit:cover;display:block;filter:grayscale(1)}
.ph.p-motorcycle img{object-position:50% 62%}
.ph.p-dog img{object-position:50% 40%}
.nm{display:block;font-family:Raleway,sans-serif;font-weight:700;font-size:12px;line-height:17px;
    letter-spacing:.04em;text-transform:uppercase;color:var(--brown)}
/* brand orange is 3.03:1 — under the floor for 12px bold — so hover is the
   deepened #A94A29 at 5.68:1. Orange stays a rule and a marker. */
a:hover .nm{color:var(--deepor)}

/* ================= C1 · grid, caption under ================= */
.c1 .g{display:grid;grid-template-columns:repeat(5,1fr);column-gap:32px;row-gap:34px}
.c1 a{display:block;text-align:center}
.c1 .ph{height:173px}
.c1 .nm{margin-top:11px}
.c1 a:hover .ph{box-shadow:0 4px 0 var(--orange)}

/* ================= C2 · grid, caption on the image =================
   The scrim is not decoration, it is the contrast. White on
   rgba(0,0,0,.86) over a MID photograph measures 8.8:1 and over the
   brightest of the thirteen 6.0:1 — both clear 4.5:1. A lighter scrim
   would fail on the bright tiles only, which is the worst kind of failure:
   it looks fine on the ones you happen to check. */
.c2 .g{display:grid;grid-template-columns:repeat(5,1fr);gap:16px}
/* position:relative on the ANCHOR, not on .ph. The name is a sibling of the
   photograph, not a child of it, so without this it positions against the
   page and lands nowhere near its tile — which is exactly what the first
   render did: thirteen scrims and not one visible name. The height measured
   correctly the whole time. */
.c2 a{display:block;position:relative}
.c2 .ph{height:230px}
.c2 .ph:after{content:"";position:absolute;inset:0;
              background:linear-gradient(transparent 34%,rgba(0,0,0,.86) 80%)}
.c2 .nm{position:absolute;left:14px;right:14px;bottom:12px;color:#fff;z-index:1;text-align:left}
.c2 a:hover .nm{color:#fff}
.c2 a:hover .ph{box-shadow:inset 0 -4px 0 var(--orange)}

/* ================= C3 · mosaic, one claim leading =================
   The arithmetic is exact and it is the reason this option exists at all:
   a 4-column grid is 16 cells over 4 rows, a 2x2 lead takes 4, and twelve
   claims fill the remaining twelve with NO hole. Thirteen fits a mosaic
   where it does not fit a plain grid. */
.c3 .g{display:grid;grid-template-columns:repeat(4,1fr);gap:32px}
.c3 a{display:block;text-align:center}
.c3 .ph{height:220px}
.c3 .lead{grid-column:span 2;grid-row:span 2}
.c3 .lead .ph{height:472px}
.c3 .lead .nm{font-size:15px;line-height:22px}
.c3 .nm{margin-top:11px}
.c3 a:hover .ph{box-shadow:0 4px 0 var(--orange)}

/* ================= C4 · rows with thumbnails ================= */
.c4 .rows{border-top:1px solid var(--line)}
.c4 a{display:flex;align-items:center;gap:22px;padding:14px 0;
      border-bottom:1px solid var(--line)}
.c4 .ph{width:96px;height:72px;flex:none}
.c4 .nm{font-size:17px;line-height:24px;letter-spacing:0;text-transform:none;
        font-family:"Open Sans",Arial,sans-serif;font-weight:400}
.c4 .ar{margin-left:auto;color:var(--orange);font-size:13px;opacity:0;
        transform:translateX(-6px);transition:opacity .14s,transform .14s}
.c4 a:hover .ar{opacity:1;transform:translateX(0)}

/* ================= C5 · a row per group =================
   Each group fills the full 1280, so the tile size IS the group size:
   six across at 193, four at 302, three at 411. The grouping becomes
   legible without a single label having to explain it — and that is also
   the cost, because the grouping is home_v1's proposal, not the firm's. */
.c5 .grp{margin-bottom:34px}
.c5 .glab{font-family:Raleway,sans-serif;font-weight:700;font-size:11px;line-height:18px;
          letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
          padding-bottom:8px;border-bottom:2px solid var(--orange);margin-bottom:18px}
.c5 .glab .n{color:#B9B2A8;margin-left:6px}
.c5 .r{display:grid;gap:24px}
.c5 .r6{grid-template-columns:repeat(6,1fr)}
.c5 .r4{grid-template-columns:repeat(4,1fr)}
.c5 .r3{grid-template-columns:repeat(3,1fr)}
.c5 a{display:block;text-align:center}
.c5 .r6 .ph{height:145px}
.c5 .r4 .ph{height:227px}
.c5 .r3 .ph{height:308px}
.c5 .nm{margin-top:10px}
.c5 a:hover .ph{box-shadow:0 4px 0 var(--orange)}

/* ================= C6 · hexagon tiles =================
   width / height = 0.8660254 exactly — 230.4 / 0.8660254 = 266.03. A hexagon
   off ratio is a bug. The cost is in the shape itself: a hexagon mask cuts
   FOUR corners off every photograph, and none of these thirteen was framed
   for it. */
.c6 .g{display:grid;grid-template-columns:repeat(5,1fr);column-gap:32px;row-gap:30px}
.c6 a{display:block;text-align:center}
.c6 .ph{height:266.03px;clip-path:polygon(50% 0,100% 25%,100% 75%,50% 100%,0 75%,0 25%)}
.c6 .nm{margin-top:12px}
.c6 a:hover .ph img{filter:grayscale(1) brightness(1.12)}

/* ================= C7 · four across =====================================
   1280 = 4 x 296 + 3 x 32, and the tile is 296 x 222 at 4:3.
   FOUR IS THE COLUMN COUNT THIRTEEN LIKES LEAST: 4 + 4 + 4 leaves ONE claim
   alone on a fourth row, next to three empty cells. Five leaves a row of
   three, which reads as a row; one tile beside three holes reads as a
   mistake. So the option is drawn three ways — the orphan as it falls, and
   the two honest cures — rather than pretending the count works. */
/* HOVER ON C7 ONLY (Adi 2026-08-31: "ketika di hover hilangkan filter
   grayscale-nya dan hanya update pada C7"). The photograph returns to
   whatever it is in the file. Worth knowing before picking it: ELEVEN of
   the thirteen sources are already black and white, so on eleven tiles
   this does nothing at all and only the cyclist and the pedestrian change
   — H·a below shows exactly that, at rest and hovered. C7·a and C7·b are
   deliberately left WITHOUT it, so the sheet shows the same layout with
   and without the behaviour. */
.c7 .ph img{transition:filter .3s ease}
.c7 a:hover .ph img{filter:grayscale(0)}
.c7 .g,.c7a .g{display:grid;grid-template-columns:repeat(4,1fr);gap:32px}
.c7 a,.c7a a,.c7b a{display:block;text-align:center}
.c7 .ph,.c7a .ph,.c7b .ph{height:222px}
.c7 .nm,.c7a .nm,.c7b .nm{margin-top:11px}
.c7 a:hover .ph,.c7a a:hover .ph,.c7b a:hover .ph{box-shadow:0 4px 0 var(--orange)}
/* CURE 1 — the first claim takes the whole top row, so 1 + 4 + 4 + 4 = 13
   with nothing left over. The row is full because the count changed, not
   because the tile was stretched to hide a hole. */
.c7a .wide{grid-column:span 4}
.c7a .wide .ph{height:300px}
.c7a .wide .nm{font-size:15px;line-height:22px}
/* CURE 2 — flexbox instead of grid, so the last row centres itself. The
   three full rows are unaffected: 4 x 296 + 3 x 32 is exactly 1280, so
   there is nothing to centre until the row that is short. */
.c7b .g{display:flex;flex-wrap:wrap;gap:32px;justify-content:center}
.c7b a{flex:0 0 296px}

/* ================= HOVER — "can the image go colour?" ====================
   The honest answer is a measurement: ELEVEN of the thirteen photographs are
   already black and white in the source file. Returning colour on hover is
   `filter:grayscale(0)`, and on eleven tiles that returns nothing at all —
   a hover that works on two of thirteen is worse than no hover, because the
   reader learns the wrong rule from the first tile they touch.
   Each option is drawn twice: the top row at rest, the bottom row hovered.
   The row is chosen to contain BOTH colour sources (cyclist, pedestrian). */
.hv .row{display:grid;grid-template-columns:repeat(5,1fr);column-gap:32px;row-gap:10px}
.hv a{display:block;text-align:center}
.hv .ph{height:173px}
.hv .nm{margin-top:10px}
.hv .rl{font-family:Raleway,sans-serif;font-weight:700;font-size:11px;letter-spacing:.16em;
        text-transform:uppercase;color:var(--muted);margin:26px 0 10px}
.hv .rl:first-child{margin-top:0}
.hv .cflag{display:block;font-size:10px;letter-spacing:.1em;color:var(--deepor);margin-top:3px}

/* Ha — the literal request: give the colour back. */
.ha a:hover .ph img,.ha a.hov .ph img{filter:grayscale(0)}
/* Hb — a brand duotone. mix-blend-mode:color takes the HUE from the orange
   layer and the LUMINANCE from the photograph, so it does not care whether
   the source had colour: all thirteen behave identically. */
.hb .ph:before{content:"";position:absolute;inset:0;background:var(--orange);
               mix-blend-mode:color;opacity:0;transition:opacity .25s ease;z-index:1}
.hb a:hover .ph:before,.hb a.hov .ph:before{opacity:1}
/* Hc — no colour at all: the photograph simply lifts. Works on thirteen for
   the same reason Hb does, and keeps the page monochrome. */
.hc a:hover .ph img,.hc a.hov .ph img{filter:grayscale(1) brightness(1.18) contrast(1.06)}

/* ======================================================================
   ROUND 2 — different families, not more variations on the grid.
   ====================================================================== */

/* ================= C8 · the wall, no gutters =============================
   Zero gap, so the thirteen become ONE photographic block rather than
   thirteen objects on a page. Five across is 256px — the home page's own
   lattice, 5 x 256 = 1280 — and the last row's three divide the same 1280
   at 426.67 each, so every row reaches both edges and the ragged row that
   every gridded option here has simply does not occur.
   Names sit on the image, on the same scrim C2 measured: white on
   rgba(0,0,0,.86) is 8.8:1 over a mid photograph, 6.0:1 over the brightest. */
.c8 .r{display:grid}
.c8 .r5{grid-template-columns:repeat(5,1fr)}
.c8 .r3{grid-template-columns:repeat(3,1fr)}
.c8 a{display:block;position:relative}
.c8 .ph{height:200px}
.c8 .r3 .ph{height:240px}
.c8 .ph:after{content:"";position:absolute;inset:0;
              background:linear-gradient(transparent 40%,rgba(0,0,0,.86) 84%)}
.c8 .nm{position:absolute;left:12px;right:12px;bottom:10px;color:#fff;z-index:1;
        text-align:left;font-size:11px;line-height:15px}
.c8 a:hover .ph{box-shadow:inset 0 -4px 0 var(--orange)}
.c8 a:hover .nm{color:#fff}

/* ================= C9 · index, one preview ===============================
   Thirteen names and ONE photograph: the picture changes with the cursor
   instead of thirteen of them competing at once. Pure CSS — the previews
   are siblings of the names, so `:nth-child(n):hover ~ :nth-child(n+13)`
   reaches the right one; no script.
   It is the only option that does not put thirteen images on the page,
   which is also the only one that fixes the 2.4 MB. */
.c9 .p{display:grid;grid-template-columns:520px 1fr;column-gap:64px;align-items:start}
.c9 .it{grid-column:1;display:flex;align-items:center;justify-content:space-between;
        font-size:16px;line-height:24px;color:var(--brown);padding:9px 0;
        border-bottom:1px solid var(--line)}
.c9 .it .no{font-family:Raleway,sans-serif;font-weight:700;font-size:11px;color:var(--muted);
            letter-spacing:.06em;margin-right:14px}
.c9 .it span.t{flex:1}
.c9 .it:hover{color:var(--deepor)}
.c9 .pv{grid-column:2;grid-row:1/span 13;width:100%;height:100%;min-height:474px;
        opacity:0;transition:opacity .25s ease}
.c9 .pv .ph{height:474px}
.c9 .pv:first-of-type{opacity:1}
.c9 .it:hover ~ .pv{opacity:0}
__C9_HOVER__

/* ================= C10 · staggered =======================================
   The same 230.4 cell as C1, but every second column starts half a tile
   lower, so the eye reads a rhythm instead of four straight rules. The
   stagger is a translate, not a margin, so the grid still lays out as a
   plain grid and nothing has to be reordered. Costs 87px of section
   height — half a tile — and nothing else. */
.c10 .g{display:grid;grid-template-columns:repeat(5,1fr);column-gap:32px;row-gap:34px;
        padding-bottom:105px}
.c10 a{display:block;text-align:center}
.c10 .ph{height:173px}
.c10 .nm{margin-top:11px}
.c10 a:nth-child(2n){transform:translateY(105px)}
.c10 a:hover .ph{box-shadow:0 4px 0 var(--orange)}

/* ================= C11 · filmstrip =======================================
   One row, scrolled sideways: 13 x 230.4 + 12 x 16 = 3,187px of strip in a
   1,280px window, so about five of the thirteen are visible at once.
   The shortest thing on the sheet by a wide margin — and the only one that
   hides items, which on an INDEX is the objection that matters. */
.c11 .strip{display:flex;gap:16px;overflow-x:auto;padding-bottom:14px}
.c11 a{flex:0 0 230.4px;display:block;text-align:center}
.c11 .ph{height:173px}
.c11 .nm{margin-top:11px}
.c11 a:hover .ph{box-shadow:0 4px 0 var(--orange)}
.c11 .strip::-webkit-scrollbar{height:8px}
.c11 .strip::-webkit-scrollbar-thumb{background:var(--line)}

/* ================= C12 · circles =========================================
   A circle keeps pi/4 = 78.5% of its frame; the hexagon in C6 keeps exactly
   3/4. So circles crop LESS than the brand shape does, which is worth
   knowing before rejecting either on cropping alone. What a circle costs
   instead is the brand: it is the one shape on this sheet that says nothing
   about this firm. */
.c12 .g{display:grid;grid-template-columns:repeat(5,1fr);column-gap:32px;row-gap:30px}
.c12 a{display:block;text-align:center}
.c12 .ph{height:230.4px;border-radius:50%}
.c12 .nm{margin-top:14px}
.c12 a:hover .ph{box-shadow:0 0 0 4px var(--orange)}

/* ================= C13 · the triage checkerboard =========================
   Adi: "tampilan yang mirip hero di home_v1". home_v1 has no hero — its
   first screen is `.triage`, a GUTTERLESS checkerboard of white type cells
   and photographs, 5 x 280px, tone alternating orange / teal, with a small
   hexagon marker that scales in under the cursor. That construction is
   lifted here part for part: same 5 columns, same 280px row, same 44x3
   rule, same marker, same alternation.

   ONE THING IS DELIBERATELY NOT COPIED, and it is a defect not a choice:
   home_v1 sets the triage headings in brand orange and brand teal at 21px
   weight 300. On white that measures 3.03:1 and 2.60:1 — and 21px at
   weight 300 is NOT large text by WCAG, so the floor is 4.5:1. Both fail;
   teal fails even the 3:1 large-text floor. Here the heading is brown
   (10.87:1) and the ORANGE AND TEAL MOVE TO THE RULE AND THE MARKER, which
   are non-text and need only 3:1. Orange marks, brown reads.

   The marker is 13 x 15.01 — 13 / 0.8660254. home_v1's is 13 x 15. */
.c13 .g{display:grid;grid-template-columns:repeat(5,1fr);grid-auto-rows:280px}
.c13 .cell{position:relative;display:block;overflow:hidden}
.c13 .cell.txt{background:#fff;padding:54px 24px 0;text-align:center;
               transition:background .18s ease}
.c13 .cell.txt .in{position:relative;z-index:1}
.c13 .cell.txt .rule{display:block;width:44px;height:3px;margin:0 auto 20px}
.c13 .cell.txt h3{font-family:Raleway,sans-serif;font-weight:300;font-size:19px;
                  letter-spacing:.12em;text-transform:uppercase;line-height:1.35;
                  color:var(--brown);transition:color .3s ease;margin:0}
.c13 .cell.txt.o .rule{background:var(--orange)}
.c13 .cell.txt.t .rule{background:var(--teal)}
.c13 .cell.txt .mk{position:absolute;left:50%;bottom:30px;width:13px;height:15.01px;
                   transform:translateX(-50%) scale(0);transition:transform .35s ease;z-index:1;
                   clip-path:polygon(50% 0,100% 25%,100% 75%,50% 100%,0 75%,0 25%)}
.c13 .cell.txt.o .mk{background:var(--orange)}
.c13 .cell.txt.t .mk{background:var(--teal)}
.c13 .cell.txt:hover{background:#FBF6F1}
.c13 .cell.txt:hover .mk{transform:translateX(-50%) scale(1)}
/* home_v1's .flip turns a type cell over on arming; the same idea here does
   the job the checkerboard would otherwise cost — a type cell reveals ITS
   OWN photograph, so no claim is left without one. White on rgba(0,0,0,.82)
   over a mid photograph measures 7.5:1 and over the brightest of the
   thirteen 5.0:1; a lighter veil would pass on the dark tiles and fail on
   the bright ones. */
.c13 .cell.txt .ph{position:absolute;inset:0;height:auto;opacity:0;z-index:0;
                   transition:opacity .3s ease}
.c13 .cell.txt .ph:after{content:"";position:absolute;inset:0;background:rgba(0,0,0,.82)}
.c13 .cell.txt:hover .ph{opacity:1}
.c13 .cell.txt:hover h3{color:#fff}
/* the photograph cells name themselves on C8's measured scrim */
.c13 .cell.pho .ph{height:100%}
.c13 .cell.pho .ph:after{content:"";position:absolute;inset:0;
                         background:linear-gradient(transparent 42%,rgba(0,0,0,.86) 86%)}
.c13 .cell.pho .nm{position:absolute;left:14px;right:14px;bottom:12px;color:#fff;z-index:1;
                   text-align:left;font-size:11px;line-height:15px}
.c13 .cell.pho:hover .ph{box-shadow:inset 0 -4px 0 var(--orange)}
.c13 .cell.pho:hover .nm{color:#fff}

/* ======================================================================
   ROUND 3 — four more families: a band, a slice, an editorial row, a poster.
   ====================================================================== */

/* ================= C14 · horizontal accordion ============================
   Thirteen full-width bands, 64px each at rest — 13 x 64 = 832 with no
   grid at all. Hovering a band opens it to 240. The picture is doing
   something no tile does here: it runs the whole 1280, so the eye reads
   the section as a stack of horizons rather than a wall of objects.
   Cost, and it is arithmetic: a 1280 x 64 band is a 20:1 crop. Twelve of
   the thirteen photographs are between 0.71:1 and 2.00:1, so at rest each
   one is a slit through the middle of its frame and mostly unreadable —
   the layout only shows a photograph while the cursor is on it. */
.c14 .bands{border-top:1px solid var(--line)}
.c14 .band{display:block;position:relative;height:64px;overflow:hidden;
           transition:height .32s cubic-bezier(.3,.7,.35,1)}
.c14 .band .ph{position:absolute;inset:0;height:auto}
.c14 .band .ph img{object-position:50% 42%}
.c14 .band .ph:after{content:"";position:absolute;inset:0;background:rgba(0,0,0,.55)}
.c14 .band:hover{height:240px}
.c14 .band .nm{position:absolute;left:24px;bottom:0;top:0;display:flex;align-items:center;
               color:#fff;z-index:1;font-size:13px;letter-spacing:.1em}
.c14 .band:hover .nm{align-items:flex-end;padding-bottom:18px}
.c14 .band .no{position:absolute;right:24px;top:0;bottom:0;display:flex;align-items:center;
               color:#fff;opacity:.55;z-index:1;font-family:Raleway,sans-serif;font-weight:700;
               font-size:11px;letter-spacing:.12em}

/* ================= C15 · vertical slices =================================
   The same idea turned ninety degrees: thirteen slices side by side across
   1280, each 96.6px at rest — (1280 - 12 gaps of 2) / 13 — opening to
   about four times that under the cursor. Height is fixed at 420, so the section is 420 whatever happens —
   the only option on the sheet whose height does not depend on thirteen.
   Cost: the names have to be rotated to fit a 98px column, and rotated
   type is slower to read; and a 96.6 x 420 sliver is a 1:4.35 crop, which
   is the narrowest anything is cropped anywhere here. */
.c15 .slices{display:flex;height:420px;gap:2px}
.c15 .sl{position:relative;flex:1 1 0;overflow:hidden;display:block;
         transition:flex-grow .32s cubic-bezier(.3,.7,.35,1)}
.c15 .sl:hover{flex-grow:4}
.c15 .sl .ph{position:absolute;inset:0;height:auto}
.c15 .sl .ph:after{content:"";position:absolute;inset:0;background:rgba(0,0,0,.42);
                   transition:background .3s ease}
.c15 .sl:hover .ph:after{background:rgba(0,0,0,.24)}
/* writing-mode, not a rotate transform. The first version rotated the box
   about its own bottom-centre, which swung half of every long name outside
   the 96.6px slice and `overflow:hidden` ate it — the names were clipped, and
   the frame's height measured correctly the whole time. vertical-rl flows the
   text INSIDE the box, so the slice can never be too narrow for it. */
.c15 .sl .nm{position:absolute;left:0;right:0;bottom:18px;z-index:1;color:#fff;
             writing-mode:vertical-rl;transform:rotate(180deg);margin:0 auto;
             max-height:calc(100% - 36px);width:max-content;
             font-size:11px;letter-spacing:.12em;line-height:1}

/* ================= C16 · editorial rows ==================================
   Photograph and copy alternating sides, one claim per row. The family
   every other option here is avoiding: it gives each claim a whole row of
   the page and a 520 x 280 photograph, which is the largest any single
   claim gets except C3's lead.
   Its cost is not a trade-off, it is a number: THIRTEEN ROWS. */
.c16 .row{display:flex;align-items:center;gap:56px;padding:22px 0;
          border-bottom:1px solid var(--line)}
.c16 .row:last-child{border-bottom:0}
.c16 .row:nth-child(2n){flex-direction:row-reverse}
.c16 .ph{flex:0 0 520px;height:280px}
.c16 .tx{flex:1}
.c16 .no{font-family:Raleway,sans-serif;font-weight:700;font-size:11px;letter-spacing:.16em;
         color:var(--muted);display:block;margin-bottom:10px}
.c16 .nm{font-family:Raleway,sans-serif;font-weight:300;font-size:28px;letter-spacing:.06em;
         text-transform:uppercase;color:var(--brown);line-height:1.25}
.c16 .row:hover .nm{color:var(--deepor)}
.c16 .go{display:inline-block;margin-top:16px;font-family:Raleway,sans-serif;font-weight:700;
         font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--deepteal)}

/* ================= C17 · the poster ======================================
   ONE photograph behind the whole section and thirteen names over it. Like
   C9 it puts a single image on the page — the 2.4 MB answered — but with
   no interaction at all, so it works the same on a phone.
   White on rgba(0,0,0,.78) over this photograph measures 6.4:1; over the
   brightest of the thirteen it would be 5.4:1, so the veil is safe whichever
   picture is chosen later. Which one that is, is a decision: right now the
   section is illustrated by car accidents alone. */
.c17 .poster{position:relative;overflow:hidden;padding:56px 0}
.c17 .poster .bg{position:absolute;inset:0;z-index:0}
.c17 .poster .bg img{width:100%;height:100%;object-fit:cover;filter:grayscale(1)}
.c17 .poster .bg:after{content:"";position:absolute;inset:0;background:rgba(0,0,0,.78)}
.c17 .cols{position:relative;z-index:1;display:grid;grid-template-columns:repeat(3,1fr);
           column-gap:48px}
.c17 a{display:flex;gap:14px;align-items:baseline;color:#fff;font-size:16px;line-height:24px;
       padding:11px 0;border-bottom:1px solid rgba(255,255,255,.18)}
.c17 a:hover{color:#F7C7B3}
.c17 a .no{font-family:Raleway,sans-serif;font-weight:700;font-size:11px;letter-spacing:.06em;
           color:rgba(255,255,255,.55);min-width:22px}


""".replace("__C9_HOVER__", C9_HOVER)


def C1():
    return '<div class="s c1"><div class="g">%s</div></div>' % "".join(
        '<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t) for t, h in CLAIMS)


def C2():
    return '<div class="s c2"><div class="g">%s</div></div>' % "".join(
        '<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t) for t, h in CLAIMS)


def C3():
    lead, rest = CLAIMS[0], CLAIMS[1:]
    assert len(rest) == 12, "the mosaic's twelve remaining cells are not twelve"
    return ('<div class="s c3"><div class="g">'
            '<a class="lead" href="%s">%s<span class="nm">%s</span></a>%s</div></div>'
            % (lead[1], img(lead[0]), lead[0],
               "".join('<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t)
                       for t, h in rest)))


def C4():
    return ('<div class="s c4"><div class="rows">%s</div></div>' % "".join(
        '<a href="%s">%s<span class="nm">%s</span>'
        '<i class="ar fa fa-arrow-right"></i></a>' % (h, img(t), t) for t, h in CLAIMS))


def C5():
    out = []
    for gh, _gr, items in GROUPS:
        n = len(items)
        assert n in (6, 4, 3), n
        out.append('<div class="grp"><div class="glab">%s<span class="n">%d</span></div>'
                   '<div class="r r%d">%s</div></div>'
                   % (gh, n, n, "".join('<a href="%s">%s<span class="nm">%s</span></a>'
                                        % (h, img(t), t) for t, h in items)))
    return '<div class="s c5">%s</div>' % "".join(out)


def C6():
    return '<div class="s c6"><div class="g">%s</div></div>' % "".join(
        '<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t) for t, h in CLAIMS)


def C7():
    return '<div class="s c7"><div class="g">%s</div></div>' % "".join(
        '<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t) for t, h in CLAIMS)


def C7a():
    lead, rest = CLAIMS[0], CLAIMS[1:]
    assert len(rest) == 12 and len(rest) % 4 == 0, "the twelve no longer fill three rows of four"
    return ('<div class="s c7a"><div class="g">'
            '<a class="wide" href="%s">%s<span class="nm">%s</span></a>%s</div></div>'
            % (lead[1], img(lead[0]), lead[0],
               "".join('<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t)
                       for t, h in rest)))


def C7b():
    return '<div class="s c7b"><div class="g">%s</div></div>' % "".join(
        '<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t) for t, h in CLAIMS)


# the five tiles that make the hover comparison honest: both COLOUR sources
# (cyclist, pedestrian) sit in this row, beside three that are already
# monochrome. A row of five monochrome tiles would make Ha look like it did
# nothing, and a row of the two colour ones would make it look like it worked.
HOVER_ROW = CLAIMS[:5]
assert [t for t, _ in HOVER_ROW][2:5:2] == ["Cyclist Accident Claims", "Pedestrian Injury Claims"]


def _hrow(cls, hov):
    return '<div class="row">%s</div>' % "".join(
        '<a class="%s" href="%s">%s<span class="nm">%s%s</span></a>'
        % ("hov" if hov else "", h, img(t), t,
           '<span class="cflag">colour source</span>' if t in COLOUR_SRC else "")
        for t, h in HOVER_ROW)


def _hopt(cls):
    return ('<div class="s hv %s"><div class="rl">at rest</div>%s'
            '<div class="rl">hovered</div>%s</div>' % (cls, _hrow(cls, False), _hrow(cls, True)))


def Ha():
    return _hopt("ha")


def Hb():
    return _hopt("hb")


def Hc():
    return _hopt("hc")


def C8():
    rows = [CLAIMS[0:5], CLAIMS[5:10], CLAIMS[10:13]]
    assert sum(len(r) for r in rows) == 13
    return '<div class="s c8">%s</div>' % "".join(
        '<div class="r r%d">%s</div>' % (len(r), "".join(
            '<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t) for t, h in r))
        for r in rows)


def C9():
    items = "".join(
        '<a class="it" href="%s"><span class="no">%02d</span><span class="t">%s</span></a>'
        % (h, i + 1, t) for i, (t, h) in enumerate(CLAIMS))
    pvs = "".join('<span class="pv">%s</span>' % img(t) for t, _h in CLAIMS)
    return '<div class="s c9"><div class="p">%s%s</div></div>' % (items, pvs)


def C10():
    return '<div class="s c10"><div class="g">%s</div></div>' % "".join(
        '<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t) for t, h in CLAIMS)


def C11():
    return '<div class="s c11"><div class="strip">%s</div></div>' % "".join(
        '<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t) for t, h in CLAIMS)


def C12():
    return '<div class="s c12"><div class="g">%s</div></div>' % "".join(
        '<a href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t) for t, h in CLAIMS)


def C13():
    cells = []
    for i, (t, h) in enumerate(CLAIMS):
        if i % 2 == 0:
            cells.append('<a class="cell pho" href="%s">%s<span class="nm">%s</span></a>'
                         % (h, img(t), t))
        else:
            tone = "o" if (i // 2) % 2 == 0 else "t"
            cells.append('<a class="cell txt %s" href="%s">%s'
                         '<span class="in"><span class="rule"></span><h3>%s</h3></span>'
                         '<span class="mk"></span></a>' % (tone, h, img(t), t))
    assert sum(1 for c in cells if "cell pho" in c) == 7
    return '<div class="s c13"><div class="g">%s</div></div>' % "".join(cells)


def C14():
    return '<div class="s c14"><div class="bands">%s</div></div>' % "".join(
        '<a class="band" href="%s">%s<span class="nm">%s</span>'
        '<span class="no">%02d</span></a>' % (h, img(t), t, i + 1)
        for i, (t, h) in enumerate(CLAIMS))


def C15():
    return '<div class="s c15"><div class="slices">%s</div></div>' % "".join(
        '<a class="sl" href="%s">%s<span class="nm">%s</span></a>' % (h, img(t), t)
        for t, h in CLAIMS)


def C16():
    return '<div class="s c16">%s</div>' % "".join(
        '<a class="row" href="%s">%s<span class="tx"><span class="no">%02d</span>'
        '<span class="nm">%s</span><span class="go">Read this claim &rarr;</span></span></a>'
        % (h, img(t), i + 1, t) for i, (t, h) in enumerate(CLAIMS))


def C17():
    lead = CLAIMS[0][0]
    cols = "".join(
        '<a href="%s"><span class="no">%02d</span><span>%s</span></a>' % (h, i + 1, t)
        for i, (t, h) in enumerate(CLAIMS))
    return ('<div class="s c17"><div class="poster">'
            '<span class="bg"><img src="/wp-content/uploads/%s" alt=""></span>'
            '<div class="cols">%s</div></div></div>' % (PHOTO[lead], cols))


OPTIONS = [
    ("C1", "grid, caption under", C1,
     "The page as it stands. Five across on the team page's bench module &mdash; 230.4px cell, "
     "32px gutter &mdash; so people and claims are drawn by <b>one</b> card rather than two that "
     "nearly match. Thirteen leaves a row of three, which reads as a row rather than an orphan. "
     "The caption band is what makes it 671px; every other grid option here spends that space "
     "differently."),
    ("C2", "grid, caption on the image", C2,
     "The name moves onto the photograph, so there is no caption band &mdash; and that room goes "
     "straight back into the picture, which grows from 173 to <b>230px tall, a third bigger</b>. It "
     "does not make the section shorter: 722 against C1's 671, because 57px of extra picture on "
     "three rows costs more than three caption bands saved. <b>The scrim is the contrast, "
     "not decoration:</b> white on <code>rgba(0,0,0,.86)</code> measures 8.8:1 over a mid "
     "photograph and 6.0:1 over the brightest of the thirteen. A lighter scrim would fail on the "
     "bright tiles only &mdash; the worst kind of failure, because it looks right on whichever "
     "ones you happen to check. Cost: a name over a picture is always harder to scan than a name "
     "on white."),
    ("C3", "mosaic, one claim leading", C3,
     "<b>The only option here where thirteen fits with no hole, and it is arithmetic:</b> a "
     "4-column grid is 16 cells over four rows, a 2&times;2 lead takes four, and the remaining "
     "twelve cells hold exactly the other twelve claims. It also answers a question the flat grid "
     "cannot: <b>the thirteen are not peers</b> &mdash; car accidents is the claim most people "
     "arrive for &mdash; and the lead tile is 624&times;472, the only place on the page a "
     "photograph is big enough to be a photograph. Cost: 1088px, and <b>which</b> claim leads is "
     "the firm's call, not a layout's."),
    ("C4", "rows with thumbnails", C4,
     "The photograph becomes a 96&times;72 thumbnail and the name becomes the row. <b>The most "
     "scannable by far</b> &mdash; thirteen names in one column, read top to bottom in one pass, "
     "with sentence-case names at 17px instead of thirteen shouted capitals. <b>It is also the "
     "tallest of the six</b> at 1314 &mdash; thirteen rows is thirteen rows. Cost: it spends the "
     "pictures. At 96px a photograph is an identifier, not an image, and four of these thirteen "
     "(the dog's jaw, the seatbelt, the crosswalk) are close crops that stop reading at that size."),
    ("C5", "a row per group", C5,
     "Each group fills the full 1280, so <b>the tile size IS the group size</b> &mdash; six across "
     "at 193px, four at 302, three at 411. The grouping becomes legible without a single label "
     "having to explain it. That is also the cost: <b>Road Accidents ends up with the smallest "
     "tiles because it has the most claims</b>, which is exactly backwards if importance is what "
     "the size is saying. And the grouping is home_v1's proposal, not the firm's &mdash; this is "
     "the option that breaks if the taxonomy changes."),
    ("C6", "hexagon tiles", C6,
     "The brand's shape carrying the photographs, on ratio: 230.4 / 0.8660254 = <b>266.03</b>. "
     "The most unmistakably League-and-Williams of the six. Cost is in the shape itself &mdash; "
     "<b>a hexagon mask cuts four corners off every photograph</b>, and none of these thirteen "
     "was framed for it; the dog and the motorcycle are already crops of crops. It is also the "
     "tallest per row, so it buys its identity with vertical space."),
    ("C7", "four across &mdash; the orphan as it falls", C7,
     "Adi asked for a four-column grid, and this is it, drawn honestly: <b>1280 = 4 &times; 296 + "
     "3 &times; 32</b>, tile 296&times;222. Four is the column count thirteen likes least &mdash; "
     "4 + 4 + 4 leaves <b>one claim alone on a fourth row beside three empty cells</b>. Five "
     "leaves a row of three, which reads as a row; one tile beside three holes reads as a "
     "mistake, and no amount of styling changes that. What the width buys is real though: at "
     "296px the tile is 28% wider than C1's 230, so the photographs are the biggest of any "
     "even grid here."),
    ("C7&middot;a", "four across, the first claim across the top", C7a,
     "<b>The orphan cured by changing the count, not by stretching a tile.</b> The first claim "
     "takes the whole top row at 1280&times;300 and the other twelve fill three rows of four "
     "exactly &mdash; 1 + 4 + 4 + 4 = 13, nothing left over. Same trick as C3 and the same "
     "question behind it: this only works if one claim genuinely leads, and <b>which</b> one is "
     "the firm's call. Cost: the band is 1280px of photograph, and only two of the thirteen "
     "sources are wide enough not to be cropped hard to fill it."),
    ("C7&middot;b", "four across, the last row centred", C7b,
     "<b>The orphan cured by moving it.</b> Flexbox rather than grid, so the short row centres "
     "itself; the three full rows are untouched, because 4 &times; 296 + 3 &times; 32 is exactly "
     "1280 and there is nothing to centre until the row that is short. The cheapest fix on the "
     "sheet &mdash; one property &mdash; and it keeps all thirteen the same size, which C7&middot;a "
     "does not. Cost: a centred single tile still announces that the count did not divide; it "
     "just does it politely."),
    ("C8", "the wall &mdash; no gutters", C8,
     "Zero gap, so the thirteen read as <b>one photographic block</b> rather than thirteen objects "
     "on a page. Five across is 256px &mdash; the home page's own lattice, 5 &times; 256 = 1280 "
     "&mdash; and the last row's three divide the same 1280 at 426.67 each, so <b>every row reaches "
     "both edges and the ragged last row simply does not occur</b>. That is a different answer to "
     "thirteen from C3's and C7&middot;a's: not change the count, change what a row is. Cost: no "
     "air at all between one accident and the next, which on this subject matter is a tonal "
     "decision as much as a layout one."),
    ("C9", "index, one preview", C9,
     "Thirteen names and <b>one</b> photograph: the picture changes with the cursor instead of "
     "thirteen competing at once. Pure CSS &mdash; the previews are siblings of the names, so "
     "<code>:nth-child(n):hover ~ :nth-child(n+13)</code> reaches the right one and no script is "
     "needed. <b>It is also the only option on the sheet that fixes the 2.4 MB</b>, because twelve "
     "of the thirteen images can be loaded lazily and most visitors will never ask for them. Cost, "
     "and it is a real one: <b>there is no hover on a phone</b>. On touch this needs a tap-to-show "
     "or it degrades to a plain list &mdash; which is C4 minus the thumbnails."),
    ("C10", "staggered", C10,
     "The same 230.4 cell as C1 with every second column half a tile lower, so the eye reads a "
     "rhythm instead of four straight rules across the section. The stagger is a "
     "<code>translate</code>, not a margin, so the grid still lays out as a plain grid and nothing "
     "has to be reordered for it. <b>Costs 105px &mdash; half a tile &mdash; and nothing else.</b> "
     "Cost that is not in pixels: a staggered grid is harder to scan than a straight one, and this "
     "is an index."),
    ("C11", "filmstrip", C11,
     "One row, scrolled sideways: 13 &times; 230.4 + 12 &times; 16 = <b>3,187px of strip in a "
     "1,280px window</b>, so about five of the thirteen are visible at once. <b>By far the "
     "shortest thing on the sheet</b>, which matters if this section has to sit under a hero and "
     "above a footer without owning the page. Cost, and on an index it is the objection that "
     "decides: <b>it hides eight of the thirteen</b>. A visitor who does not drag never learns "
     "the firm handles dog attacks."),
    ("C12", "circles", C12,
     "A different shape family from C6's hexagons, and the arithmetic is worth having before "
     "either is rejected on cropping: <b>a circle keeps &pi;/4 = 78.5% of its frame; the hexagon "
     "keeps exactly 3/4</b>. Circles crop <i>less</i> than the brand shape does. What a circle "
     "costs instead is the brand &mdash; it is the one shape on this sheet that says nothing about "
     "this firm &mdash; and it suits faces, which is what circles are usually for, better than it "
     "suits a crashed car."),
    ("C13", "the triage checkerboard", C13,
     "home_v1 has no hero &mdash; its first screen is <code>.triage</code>, a <b>gutterless</b> "
     "checkerboard of white type cells and photographs, 5 &times; 280px, tone alternating orange "
     "and teal, with a hexagon marker that scales in under the cursor. That construction is lifted "
     "part for part. Seven claims arrive as a photograph and six as a word, and <b>which six is "
     "decided by position, not importance</b> &mdash; so, borrowing the triage's own flip, a type "
     "cell reveals <b>its own</b> photograph on hover and no claim is left without one. "
     "<b>One thing is deliberately not copied.</b> home_v1 sets these headings in brand orange and "
     "brand teal at 21px weight 300: on white that is <b>3.03:1</b> and <b>2.60:1</b>, and 21px at "
     "weight 300 is not large text, so the floor is 4.5:1. Both fail; teal fails even the 3:1 "
     "large-text floor. Here the heading is brown (10.87:1) and <b>the orange and teal move to the "
     "rule and the marker</b>, which are non-text and need only 3:1. The alternation survives, the "
     "failure does not &mdash; and the same fix is owed to the homepage. Cost beyond the checkerboard: <b>thirteen still does not divide by five</b>, so the block ends two cells short on the third row. C8 cures that by letting the last row divide the full width; doing the same here would break the 256px module the triage is built on, so it is left visible rather than fixed with a trick borrowed from another option."),
    ("C14", "horizontal accordion", C14,
     "Thirteen full-width bands, 64px each at rest &mdash; <b>13 &times; 64 = 832 with no grid at "
     "all</b> &mdash; opening to 240 under the cursor. The picture does something no tile here "
     "does: it runs the whole 1280, so the section reads as a stack of horizons rather than a wall "
     "of objects. Cost is arithmetic: <b>a 1280 &times; 64 band is a 20:1 crop</b>, and twelve of "
     "the thirteen sources are between 0.71:1 and 2.00:1, so at rest each one is a slit through "
     "the middle of its frame. The layout only shows a photograph while the cursor is on it."),
    ("C15", "vertical slices", C15,
     "The same idea turned ninety degrees: thirteen slices side by side, <b>96.6px</b> each at rest &mdash; (1280 &minus; 12 gaps of 2) / 13 &mdash; "
     "opening to about four times that. <b>The only option on the sheet whose height does not "
     "depend on thirteen</b> &mdash; it is 420 whatever the count becomes, which is worth knowing "
     "for a practice area that keeps growing. Cost: the names have to be set vertically to fit a 96.6px "
     "column and vertical type is slower to read; and a 96.6 &times; 420 sliver is <b>a 1:4.35 crop, "
     "the narrowest anything is cropped anywhere on this sheet</b>."),
    ("C16", "editorial rows", C16,
     "Photograph and copy alternating sides, one claim per row. The family every other option here "
     "is avoiding: each claim gets a whole row of the page and a <b>520 &times; 280</b> photograph "
     "&mdash; the largest any single claim gets except C3's lead. Its cost is not a trade-off, it "
     "is a number: <b>thirteen rows</b>. See the table. It is the layout for a page whose job is "
     "to be read through, and an index's job is to be scanned."),
    ("C17", "the poster", C17,
     "<b>One</b> photograph behind the whole section and thirteen names over it. Like C9 it puts a "
     "single image on the page &mdash; the 2,427 KB answered &mdash; but with <b>no interaction at "
     "all</b>, so it behaves the same on a phone, which is exactly where C9 fails. White on "
     "<code>rgba(0,0,0,.78)</code> measures <b>6.4:1</b> over this photograph and would be 5.4:1 "
     "over the brightest of the thirteen, so the veil is safe whichever picture is chosen later. "
     "<b>Which one that is, is a decision</b> &mdash; right now the whole of injury law is "
     "illustrated by a car accident."),
    ("H&middot;a", "hover &rarr; the colour comes back", Ha,
     "The literal request, and <b>the measurement is the answer</b>: eleven of the thirteen "
     "photographs are already black and white <i>in the source file</i>, so "
     "<code>filter:grayscale(0)</code> returns nothing on eleven of them. Look at the hovered row "
     "&mdash; only the cyclist and the pedestrian change. <b>A hover that works on two tiles in "
     "thirteen is worse than no hover</b>, because the reader learns the rule from whichever tile "
     "they touch first and the other eleven then look broken."),
    ("H&middot;b", "hover &rarr; a brand duotone", Hb,
     "<b>What the request actually wants, delivered on all thirteen.</b> An orange layer with "
     "<code>mix-blend-mode:color</code> takes its HUE from the brand and its LUMINANCE from the "
     "photograph, so it does not care whether the source had colour: every tile warms identically. "
     "It is also the only version that is <i>the firm's</i> colour rather than whatever the stock "
     "photograph happened to be shot in. Cost: it is a strong effect, and orange over a dark "
     "photograph goes muddy &mdash; the dog and the passenger tiles are the ones to judge it on."),
    ("H&middot;c", "hover &rarr; the photograph lifts", Hc,
     "No colour at all: <code>brightness(1.18) contrast(1.06)</code>, so the tile brightens under "
     "the cursor. Works on thirteen for the same reason H&middot;b does &mdash; it asks nothing of "
     "the source &mdash; and it keeps the page monochrome, which is the tone every other page in "
     "this rebuild is already in. The quietest of the three, and the only one that survives the "
     "November shoot changing every photograph."),
]

HEAD = """<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<title>League Law &mdash; .claims layout options</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;500;600;700;800&amp;family=Open+Sans:wght@400;600;700&amp;display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>__CSS__</style>
</head>
<body>

<div class="page-head">
  <h1><code>.claims</code> &mdash; layout options</h1>
  <p>Nineteen ways to draw the thirteen injury claims. <b>Round&nbsp;1</b> (C1&ndash;C7&middot;b) works the grid, with the four-column version drawn three times because of what four does to thirteen. <b>Round&nbsp;2</b> (C8&ndash;C13) changes the family &mdash; a wall with no gutters, an index with one preview, a stagger, a strip, a circle, and home_v1's own checkerboard. <b>Round&nbsp;3</b> (C14&ndash;C17) leaves the tile behind altogether: a band, a slice, an editorial row, a poster. <b>H&middot;a&ndash;H&middot;c</b> at the end are not layouts: they are the hover question, drawn at rest and hovered. Every option carries the real thirteen, their
  real URLs, and <b>the photograph the live claim page itself uses</b> for each &mdash; all executed
  out of <code>gen_injuries_index.py</code>, so nothing here can drift from
  <code>injuries-index.html</code>.</p>

  <div class="box warn"><b>Thirteen is the constraint, and nothing styles its way out.</b>
  Thirteen is prime, so every plain grid leaves a ragged last row: <b>four across leaves ONE tile
  beside three empty cells</b>, five leaves a row of three, six leaves a lone tile on row three.
  A row of three reads as a row; one tile beside three holes reads as a mistake &mdash; which is
  why the four-column grid is drawn three times below rather than once.
  <br><br><b>Two options fit exactly, and both do it the same way: by changing the count, not the
  tile.</b> C3 &mdash; a 4-column grid is 16 cells over four rows, a 2&times;2 lead takes four, and
  the remaining twelve hold exactly the other twelve claims. C7&middot;a &mdash; one claim takes the
  whole top row, and 1 + 4 + 4 + 4 = 13. Both work only if one claim genuinely leads, and
  <b>which</b> one is the firm's call, not a layout's. C7&middot;b is the third answer: leave the
  count alone and centre the short row &mdash; one property, all thirteen the same size, and the
  orphan is still an orphan, just a polite one.
  <table>
    <tr><th>option</th><th>height at 1280</th><th>tile</th><th>last row</th></tr>
    <tr><td>C1 &mdash; grid, caption under</td><td>__H1__</td><td>230&times;173</td><td>three of five</td></tr>
    <tr><td>C2 &mdash; caption on the image</td><td>__H2__</td><td>230&times;230</td><td>three of five</td></tr>
    <tr><td>C6 &mdash; hexagon tiles</td><td>__H6__</td><td>230&times;266</td><td>three of five</td></tr>
    <tr><td>C5 &mdash; a row per group</td><td>__H5__</td><td>193 / 302 / 411</td><td>each row is full</td></tr>
    <tr><td>C3 &mdash; mosaic, one leading</td><td>__H3__</td><td>296&times;220, lead 624&times;472</td><td><b>no hole at all</b></td></tr>
    <tr><th colspan="4" style="background:#F6F4EF">four columns &mdash; 1280 = 4 &times; 296 + 3 &times; 32</th></tr>
    <tr><td>C7 &mdash; four across, as it falls</td><td>__H7__</td><td>296&times;222</td><td><b>one beside three holes</b></td></tr>
    <tr><td>C7&middot;b &mdash; last row centred</td><td>__H7b__</td><td>296&times;222</td><td>one, centred</td></tr>
    <tr><td>C7&middot;a &mdash; first claim across the top</td><td>__H7a__</td><td>296&times;222, band 1280&times;300</td><td><b>no hole at all</b></td></tr>
    <tr><td>C4 &mdash; rows with thumbnails</td><td>__H4__</td><td>96&times;72</td><td>one column, no ragged row</td></tr>
  </table>
  <b>C7 and C7&middot;b are the same height to the pixel (1096)</b> &mdash; centring the short row
  costs nothing but does not save anything either.
  Measured in headless Chrome at 1440, not estimated &mdash; and the first draft of this table   was wrong in two places, which is why it is measured: C2 <i>looked</i> like the short one   because it has no caption band, and C3 <i>looked</i> like the tall one because its lead   tile is the biggest thing on the sheet. Neither is true.</div>

  <div class="box"><b>Two things are settled and identical in all six, so the comparison is about
  layout.</b> <b>Tone:</b> eleven of the thirteen photographs are already black and white on the
  live site &mdash; measured as mean per-pixel chroma, not read off filenames like
  <code>blackandwhitemiata</code> &mdash; so all thirteen are filtered to match rather than the
  cyclist and the pedestrian reading as mistakes. <b>Colour:</b> names brown <b>10.87:1</b>, hover
  <code>#A94A29</code> <b>5.68:1</b>. Brand orange is 3.03:1 and carries no word anywhere here; it
  is a rule under a hovered tile and nothing else.</div>

  <div class="box"><b>The two portrait photographs are anchored, not centred.</b> The motorcycle
  source is a rider's-eye view with the machine in the <b>lower</b> half &mdash; centred, the tile
  showed an empty road and no motorcycle &mdash; and the dog's open jaw centres slightly high.
  Every option inherits both anchors, so no option is being judged on a crop the others do not
  have.</div>

  <div class="box warn"><b>Page weight, the same in five of six.</b> The tiles display at 230px and
  the media library's nearest derivative is 768px &mdash; 3.3&times; what is needed, <b>2,427 KB
  for thirteen images</b>. The 300px set is 379 KB but soft on a retina screen, and the originals
  are 6,942 KB (Pedestrian's alone is 5472&times;3648 and 3.78 MB). The size that is actually right,
  ~461px, <b>does not exist</b>: registering a 460px image size in WordPress is the fix, and it is a
  setting rather than a layout decision. <b>C4 is the exception</b> &mdash; at 96&times;72 the 300px
  set is already more than enough, so that option could ship at a tenth of the weight today.</div>
</div>
"""


def build():
    body = []
    for tag, name, fn, note in OPTIONS:
        key = tag.replace("C", "").replace("&middot;", "")
        body.append('<div class="frame">\n'
                    '  <div class="oh"><span class="tag">%s</span><span class="nm">%s</span>'
                    '<span class="h">height __H%s__</span></div>\n'
                    '  <p class="on">%s</p>\n'
                    '  <div class="stage">%s</div>\n</div>\n' % (tag, name, key, note, fn()))
    html = HEAD.replace("__CSS__", CSS) + "\n".join(body) + "\n</body>\n</html>\n"
    n = len(OPTIONS)
    assert html.count('class="stage"') == n
    for t, _, _, _ in OPTIONS:
        key = t.replace("C", "").replace("&middot;", "")
        assert "__H%s__" % key in html, key
        v = HEIGHTS[key]
        html = html.replace("__H%s__" % key, "%dpx" % v if v else "two rows, hover round")
    assert not re.search(r"__H[\da-z]+__", html)
    links = re.findall(r'<a [^>]*?href="([^"]*)"', html)
    # nine layout frames of thirteen, plus three hover frames of five drawn twice
    assert len(links) == 19 * 13 + 3 * 10, len(links)
    assert all(l.startswith("/our-services/bc-injury-claims/") for l in links)
    # every frame carries one image per link, except C17: thirteen names over a
    # SINGLE background photograph, which is the whole point of that option.
    assert html.count("<img ") == len(links) - 12
    # the hovered row must actually differ from the resting row
    assert html.count('class="hov"') == 15
    return html


if __name__ == "__main__":
    html = build()
    dest = sys.argv[1] if len(sys.argv) > 1 else OUT
    io.open(dest, "w", encoding="utf-8").write(html)
    print("wrote %s (%d bytes)" % (dest, len(html)))
