
  :root {
    --bg:#0b0d12; --card:#141720; --raise:#1b1f2a;
    --line:rgba(255,255,255,0.09); --line2:rgba(255,255,255,0.17);
    --t1:#f3f4f6; --t2:rgba(243,244,246,0.64); --t3:rgba(243,244,246,0.40);
    --go:#fff101; --ok:#3fa66a; --bad:#e04b4b; --warn:#e0a325;
    --font:'Archivo',-apple-system,'Segoe UI',system-ui,sans-serif;
    --display:'Saira Condensed','Arial Narrow',sans-serif;
    --side:236px; --tabs:62px;
  }
  * { box-sizing:border-box; margin:0; padding:0; }
  html { -webkit-text-size-adjust:100%; }

  /* SCROLLBARS, and they are part of the design rather than something
     the browser is allowed to decide. Windows Chromium draws its own:
     a 17px light-grey trough with arrow buttons at each end, which on
     a dark page is a bright strip down the sidebar and a grey plank
     under every horizontal strip. Reported as "why this scroll bar on
     desktop", twice, about two different scrollers -- which is what a
     default looks like when nobody has said otherwise.

     Thin, no buttons, no trough, and the thumb only as loud as a rule
     on this page: a scrollbar is there to say how much more there is,
     not to be looked at. It brightens on hover, because a scrollbar
     nobody can grab is the other failure. Firefox gets the two-value
     `scrollbar-*` properties; Chromium and Safari get the pseudos. */
  * { scrollbar-width:thin; scrollbar-color:var(--line2) transparent; }
  ::-webkit-scrollbar { width:9px; height:9px; }
  ::-webkit-scrollbar-track { background:transparent; }
  ::-webkit-scrollbar-corner { background:transparent; }
  ::-webkit-scrollbar-thumb {
    background:rgba(243,244,246,0.16); border-radius:99px;
    border:2px solid transparent; background-clip:padding-box; }
  ::-webkit-scrollbar-thumb:hover { background:rgba(243,244,246,0.30);
    background-clip:padding-box; border:2px solid transparent; }
  /* the buttons at the ends of a Windows scrollbar. `display:none` on
     the pseudo is the only way to be rid of them. */
  ::-webkit-scrollbar-button { display:none; }
  body { background:var(--bg); color:var(--t1); font-family:var(--font);
         font-size:14px; line-height:1.45; min-height:100vh; }
  button, input, textarea, select { font:inherit; color:inherit; }
  a { color:inherit; }
  img { max-width:100%; }
  ::selection { background:rgba(255,241,1,0.35); }

  /* ---------- the frame: a sidebar on a laptop, a tab bar on a phone ---- */
  .side { position:fixed; left:0; top:0; bottom:0; width:var(--side);
          background:var(--card); border-right:1px solid var(--line);
          display:flex; flex-direction:column; padding:18px 14px 16px;
          overflow-y:auto; z-index:6; }
  /* the mark itself is in brand.WORDMARK_CSS, which the login page shares.
     This is only where the sidebar puts it. */
  .brand { font-size:27px; padding:4px 8px 18px; color:var(--t1); }
  .who { display:flex; align-items:center; gap:10px; padding:10px 10px;
         border:1px solid var(--line); border-radius:12px; background:var(--bg);
         margin-bottom:10px; text-decoration:none; min-width:0; }
  .who img { width:26px; height:30px; object-fit:contain; flex:none; }
  .who .mark { width:26px; height:26px; border-radius:7px; flex:none; }
  .who b { display:block; font-size:13.5px; font-weight:700; line-height:1.2;
           overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .who span { display:block; font-size:11px; color:var(--t3); margin-top:2px; }
  .who > div { min-width:0; }
  .newpost { display:flex; align-items:center; justify-content:center; gap:8px;
             background:var(--go); color:#111; font-weight:800; border-radius:10px;
             padding:11px 12px; text-decoration:none; font-size:14px;
             margin-bottom:12px; }
  .newpost:hover { background:#fff44d; }
  .newpost svg { width:16px; height:16px; stroke:#111; fill:none;
                 stroke-width:2.6; stroke-linecap:round; }
  .nav { display:flex; flex-direction:column; flex:1; }
  .nav a { display:flex; align-items:center; gap:11px; padding:9px 10px;
           border-radius:9px; text-decoration:none; color:var(--t2);
           font-size:13.5px; font-weight:500; }
  .nav a svg, .tabs svg, .sheet svg {
    width:17px; height:17px; flex:none; stroke:currentColor; fill:none;
    stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; opacity:.85; }
  .nav a:hover { color:var(--t1); background:rgba(255,255,255,0.04); }
  .nav a.on { color:var(--t1); background:rgba(255,255,255,0.08); font-weight:700; }
  .nav a.on svg { opacity:1; }
  .nav .dot { width:9px; height:9px; border-radius:50%; flex:none; margin:0 4px; }
  .n { margin-left:auto; background:var(--go); color:#111; font-size:11px;
       font-weight:800; padding:2px 7px; border-radius:999px; min-width:20px;
       text-align:center; line-height:1.4; }
  .nav h6 { font-size:10.5px; letter-spacing:1.3px; text-transform:uppercase;
            color:var(--t3); font-weight:700; padding:16px 10px 6px; }
  .nav .grow { flex:1; min-height:12px; }
  .fine { color:var(--t3); font-size:11.5px; line-height:1.5; padding:12px 10px 0; }

  .top, .tabs, #more, #whichclub { display:none; }
  .main { margin-left:var(--side); min-height:100vh; }
  .wrap { max-width:960px; margin:0 auto; padding:30px 32px 60px; }

  @media (max-width:900px) {
    .side { display:none; }
    .main { margin-left:0; padding-bottom:calc(var(--tabs) + 16px); }
    .wrap { padding:18px 16px 40px; }
    /* OPAQUE. This bar was once rgba(0,0,0,0.25) and the page scrolled
       straight through it; a sticky bar has to be opaque or it is a window. */
    .top { display:flex; position:sticky; top:0; z-index:5; background:var(--bg);
           border-bottom:1px solid var(--line); align-items:center; gap:10px;
           padding:0 16px; height:54px; }
    .top .brand { padding:0; font-size:22px; }
    .top .club { margin-left:auto; display:flex; align-items:center; gap:8px;
                 font-size:13px; font-weight:700; text-decoration:none;
                 color:var(--t1); min-width:0; background:none; border:none;
                 cursor:pointer; padding:6px 8px; margin-right:-8px;
                 font-family:inherit; }
    .top .club img { width:20px; height:24px; object-fit:contain; }
    .top .club svg { width:15px; height:15px; stroke:var(--t3); fill:none;
                 stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round;
                 flex:none; }
    .top .club span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    .tabs { display:flex; position:fixed; left:0; right:0; bottom:0;
            height:calc(var(--tabs) + env(safe-area-inset-bottom));
            padding-bottom:env(safe-area-inset-bottom);
            background:var(--card); border-top:1px solid var(--line); z-index:6; }
    .tabs a, .tabs button { flex:1; display:flex; flex-direction:column;
            align-items:center; justify-content:center; gap:3px; position:relative;
            text-decoration:none; color:var(--t3); font-size:10.5px; font-weight:600;
            background:none; border:none; cursor:pointer; }
    .tabs svg { width:22px; height:22px; }
    .tabs .on { color:var(--t1); }
    .tabs .on svg { opacity:1; }
    .tabs .n { position:absolute; top:7px; left:calc(50% + 6px); margin:0;
               font-size:10px; padding:1px 5px; min-width:17px; }
    .tabs .post { color:var(--t1); }
    .tabs .post .ic { width:46px; height:46px; border-radius:14px; background:var(--go);
            display:flex; align-items:center; justify-content:center; margin-top:-20px;
            box-shadow:0 8px 20px rgba(255,241,1,0.22); }
    .tabs .post .ic svg { stroke:#111; stroke-width:2.6; opacity:1; width:22px; height:22px; }
    #more, #whichclub { position:fixed; inset:0; z-index:40;
            background:rgba(6,7,10,0.62); }
    #more.on, #whichclub.on { display:block; }
    /* the switcher's rows carry a badge where More's carry an icon */
    #whichclub .sheet img { width:22px; height:26px; object-fit:contain; flex:none; }
    #whichclub .sheet a.on { background:rgba(255,241,1,0.08); }
    #whichclub .sheet i { font-style:normal; font-weight:500; color:var(--t3);
            font-size:12.5px; margin-left:6px; }
    #whichclub .sheet h6 { font-size:10.5px; letter-spacing:1.3px;
            text-transform:uppercase; color:var(--t3); font-weight:700;
            padding:4px 10px 8px; margin:0; }
    /* A DEPLOYMENT WITH TWENTY CLUBS IS THE POINT OF THIS CONTROL. The
       sheet is pinned to the bottom of the screen, so without a limit the
       twentieth club is above the top of it and cannot be reached at all. */
    #whichclub .sheet .scroll { max-height:56vh; overflow-y:auto;
            -webkit-overflow-scrolling:touch; }
    .sheet { position:absolute; left:0; right:0; bottom:0; background:var(--card);
             border-radius:18px 18px 0 0; border-top:1px solid var(--line2);
             padding:12px 12px calc(18px + env(safe-area-inset-bottom)); }
    .sheet .grab { width:36px; height:4px; border-radius:99px; background:var(--line2);
                   margin:0 auto 10px; }
    .sheet a { display:flex; align-items:center; gap:12px; padding:13px 10px;
               border-radius:10px; text-decoration:none; color:var(--t1);
               font-size:15px; font-weight:600; }
    .sheet a svg { width:19px; height:19px; color:var(--t2); }
    .sheet a.on { background:rgba(255,255,255,0.07); }
    .sheet a .dot { width:9px; height:9px; border-radius:50%; flex:none; margin:0 5px; }
    .sheet .fine { padding:10px 10px 0; }
  }

  /* ---------- the words on every page ---------------------------------- */
  .page-head { display:flex; align-items:flex-end; justify-content:space-between;
               gap:16px; flex-wrap:wrap; margin-bottom:22px; }
  .page-title { font-size:26px; font-weight:800; letter-spacing:-.3px; line-height:1.15; }
  .lede { color:var(--t2); font-size:14px; line-height:1.55; margin-top:6px; max-width:64ch; }
  .lede a { color:var(--go); }
  h2 { font-size:11.5px; letter-spacing:1.3px; text-transform:uppercase;
       color:var(--t3); font-weight:700; margin:30px 0 12px; }
  h2 i { text-transform:none; letter-spacing:0; font-style:normal; font-weight:500; }
  .eyebrow { color:var(--t3); font-size:11px; letter-spacing:1.2px;
             text-transform:uppercase; font-weight:700; margin-bottom:8px; }
  .hint { color:var(--t3); font-size:12.5px; line-height:1.5; }
  .hint a, .hint button.link { color:var(--go); }
  .err { color:var(--bad); font-size:13.5px; line-height:1.5; }
  .err:empty { display:none; }
  .warn { color:var(--warn); font-size:13px; line-height:1.5; }
  .empty { color:var(--t3); font-style:italic; padding:22px 0; font-size:14px; }
  code { font-family:ui-monospace,Menlo,Consolas,monospace; font-size:12.5px;
         background:rgba(255,255,255,0.06); padding:1px 5px; border-radius:5px; }

  /* ---------- surfaces ------------------------------------------------- */
  .card { background:var(--card); border:1px solid var(--line); border-radius:14px;
          padding:18px; margin-bottom:12px; }
  .card.hot { border-color:var(--bad); }
  .card.lit { border-color:var(--go); }
  .card.warned { border-color:var(--warn); }
  .row { display:flex; gap:12px; align-items:center; background:var(--card);
         border:1px solid var(--line); border-radius:12px; padding:12px 14px;
         margin-bottom:8px; flex-wrap:wrap; }
  .row.hot { border-color:var(--bad); }
  .lrow { display:flex; align-items:center; gap:10px; padding:10px 0;
          border-top:1px solid var(--line); font-size:13.5px; text-decoration:none;
          color:var(--t1); }
  .lrow > span:first-child { flex:1; min-width:0; }
  .lrow i { font-style:normal; color:var(--t3); }
  .lrow .sp { margin-left:auto; }
  .lrow.hot { color:var(--bad); }
  .capbox { background:rgba(0,0,0,0.28); border:1px dashed var(--line2);
            border-radius:10px; padding:12px 14px; font-size:13.5px; color:var(--t2);
            white-space:pre-wrap; line-height:1.55; }
  .thumbs .leave { display:inline-flex; flex-direction:column; gap:6px; flex:none; }
  .thumbs { display:flex; gap:8px; overflow-x:auto; padding-bottom:6px;
            -webkit-overflow-scrolling:touch; }
  .thumbs img { height:150px; border-radius:9px; border:1px solid var(--line);
                flex:none; background:#000; }

  /* ---------- controls ------------------------------------------------- */
  .btn { display:inline-flex; align-items:center; justify-content:center; gap:7px;
         border:1px solid transparent; border-radius:10px; padding:10px 18px;
         font-weight:700; font-size:14px; cursor:pointer; text-decoration:none;
         line-height:1.2; white-space:nowrap; background:rgba(255,255,255,0.08);
         color:var(--t1); }
  .btn:hover { background:rgba(255,255,255,0.12); }
  .btn.go { background:var(--go); color:#111; }
  .btn.go:hover { background:#fff44d; }
  .btn.ghost { background:transparent; border-color:var(--line2); }
  .btn.ghost:hover { background:rgba(255,255,255,0.05); }
  /* QUIET, NOT DISABLED. At --t3 on the faintest border Skip read as a
     button that could not be pressed; it is a lesser choice, not no choice */
  .btn.quiet { background:transparent; color:var(--t2); border-color:var(--line2); }
  .btn.quiet:hover { color:var(--t1); background:transparent; }
  .btn.sm { padding:7px 13px; font-size:13px; border-radius:8px; }
  .btn.xs { padding:5px 10px; font-size:12px; border-radius:7px; }
  .btn.link { background:none; border:none; padding:0; color:var(--t2);
              text-decoration:underline; font-weight:500; }
  .btn.link:hover { color:var(--t1); }
  .btn:disabled { opacity:.45; cursor:default; }
  .btn.go:disabled { background:var(--line2); color:var(--t1); }
  .actions { display:flex; gap:8px; flex-wrap:wrap; align-items:center; }
  .chip { display:inline-flex; align-items:center; gap:6px; border:1px solid var(--line2);
          background:transparent; color:var(--t1); border-radius:999px;
          padding:8px 15px; font-weight:600; font-size:13.5px; cursor:pointer;
          text-decoration:none; line-height:1.2; }
  .chip:hover { background:rgba(255,255,255,0.05); }
  .chip.on { background:var(--go); border-color:var(--go); color:#111; font-weight:700; }
  .chip.sm { padding:6px 12px; font-size:12.5px; }
  .chip:disabled { opacity:.4; cursor:default; }
  .chips { display:flex; gap:8px; flex-wrap:wrap; }
  .input { width:100%; background:rgba(0,0,0,0.32); color:var(--t1);
           border:1px solid var(--line2); border-radius:10px; padding:11px 13px;
           font-size:16px; outline:none; }
  .input:focus { border-color:rgba(255,241,1,0.6);
                 box-shadow:0 0 0 3px rgba(255,241,1,0.12); }
  textarea.input { min-height:96px; resize:vertical; line-height:1.45; }
  input[type=range] { accent-color:var(--go); }
  /* A DATE FIELD ON A DARK PAGE IS A HIDDEN DATE FIELD. Chromium draws the
     calendar button in near-black, so on #0b0d12 the one thing that opens
     the picker is invisible, and the only way in is to type into three
     little segments -- on a phone, with a thumb. `color-scheme` makes the
     browser draw its own control for a dark page, which also gives us a
     dark calendar popover instead of a white flash. The icon is then
     brightened and given a real target, and `openPicker` below makes the
     whole field one. */
  input[type=date] { color-scheme:dark; cursor:pointer; }
  input[type=date]::-webkit-calendar-picker-indicator {
    filter:invert(1) opacity(.62); cursor:pointer; padding:4px; margin-left:4px; }
  input[type=date]:hover::-webkit-calendar-picker-indicator { filter:invert(1) opacity(1); }
  /* the day, and the two the operator almost always wants */
  .when-row { display:flex; gap:8px; flex-wrap:wrap; align-items:center; }
  .when-row .input { max-width:190px; }
  .label { display:flex; justify-content:space-between; color:var(--t2);
           font-size:12.5px; font-weight:600; margin-bottom:6px; }
  .label i { font-style:normal; font-weight:400; color:var(--t3); }
  .field { margin-bottom:14px; }

  .tot { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:22px; }
  .pill { background:var(--card); border:1px solid var(--line); border-radius:999px;
          padding:6px 13px; font-size:12.5px; color:var(--t2); }
  .pill b { color:var(--t1); font-size:13.5px; }
  .pill.hot { border-color:var(--bad); }
  .tag { font-size:10.5px; letter-spacing:1.1px; text-transform:uppercase;
         font-weight:800; padding:4px 9px; border-radius:999px; flex:none; }
  .tag.draft { background:rgba(255,255,255,0.10); color:var(--t1); }
  .tag.ok { background:var(--go); color:#111; }
  .tag.warn { background:rgba(224,163,37,0.20); color:var(--warn); }
  .tag.bad { background:var(--bad); color:#fff; }
  .tag.muted { background:rgba(255,255,255,0.05); color:var(--t3); }
  a.plain { color:inherit; text-decoration:none; }

  /* THE NUMBERS. Set in the cards' display face, because a number that
     says "2 waiting for you" is the one thing on the home page the operator
     came to read and it should be readable from across a kitchen. */
  .stats { display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr));
           gap:10px; margin-bottom:22px; }
  .stat { background:var(--card); border:1px solid var(--line); border-radius:14px;
          padding:14px 16px 13px; text-decoration:none; color:inherit; }
  .stat b { display:block; font-family:var(--display); font-weight:800; font-size:40px;
            line-height:1; letter-spacing:.3px; }
  .stat span { display:block; color:var(--t2); font-size:12.5px; margin-top:6px; }
  /* A card that lifts under the cursor is a card you can tap. `.flat` is
     the one that cannot, and it says so by being a panel rather than a
     card: no border, no hover, nothing to aim at. Only the "clubs" counter
     is flat now -- see the note by `stat()` about where the others go. */
  a.stat:hover { border-color:var(--line2); background:var(--raise); }
  a.stat:hover span { color:var(--t1); }
  .stat.flat { background:transparent; border-style:dashed; }
  .stat.lit b { color:var(--go); }
  .stat.hot { border-color:var(--bad); } .stat.hot b { color:var(--bad); }
  .stat.zero b { color:var(--t3); }
  @media (max-width:560px) {
    .stats { grid-template-columns:repeat(3,1fr); gap:8px; }
    .stat { padding:12px 12px 11px; }
    .stat b { font-size:32px; }
    .stat span { font-size:11.5px; }
  }

  /* THE CLUB LIST IS AN ANCHOR. The home page's counters jump here when
     there is more than one club, and `.top` is 54px of sticky bar, so
     without this the first club card lands underneath it and the jump
     looks like it did nothing. */
  #clubs { scroll-margin-top:70px; }

  .clubhead { display:flex; gap:12px; align-items:center; flex-wrap:wrap; }
  .clubhead img { width:30px; height:34px; object-fit:contain; flex:none; }
  .clubhead b { font-size:18px; font-weight:800; display:block; line-height:1.2; }
  .clubhead span { color:var(--t2); font-size:13px; display:block; margin-top:3px; }
  .clubhead .tags { margin-left:auto; display:flex; gap:6px; flex-wrap:wrap; }

  /* A FOLDED GROUP OF CLUBS on Home. Quieter than a card on purpose: the
     row is a lid, and what a lid must not do is compete with the clubs
     above it for the eye. */
  .fold { margin-top:14px; }
  .foldbar { display:flex; align-items:center; gap:12px; width:100%;
             text-align:left; background:none; color:var(--t1); cursor:pointer;
             border:1px dashed var(--line2); border-radius:12px;
             padding:13px 34px 13px 14px; font-family:inherit;
             position:relative; flex-wrap:wrap; }
  .foldbar:hover { border-color:var(--t3); }
  .fold.on .foldbar { border-style:solid; border-radius:12px 12px 0 0;
                      border-bottom-color:transparent; }
  .foldbar .tw { min-width:0; flex:1; }
  .foldbar .tw b { display:block; font-weight:800; font-size:14.5px; }
  /* `> span` AND NOT `span`. The count chip is a span too, inside the
     <b>, and `.foldbar .tw span` is a class-class-element selector which
     beats `.foldbar .ct` at two classes: the chip came out as a 308px
     grey block on its own line. Same trap as the league table's header
     row and the archive tile before it. */
  .foldbar .tw > span { display:block; font-size:12.5px; margin-top:3px;
                        color:var(--t2); }
  .foldbar .ct { display:inline-block; background:var(--line2); color:var(--t1);
                 font-size:11.5px; font-weight:800; border-radius:999px;
                 padding:1px 8px; margin-left:6px; vertical-align:1px; }
  /* THE CHEVRON IS PINNED, not a flex item. As a sibling it is the last
     thing on the row, so the moment the words or the tags wrapped it went
     with them and ended up alone at the bottom right of the bar, reading
     as a stray character rather than as the thing that opens the group.
     Pinned, it is in the same place at every width and the padding-right
     above is the room it needs. */
  .foldbar .chev { color:var(--t3); font-size:12px; position:absolute;
                   right:14px; top:50%; transform:translateY(-50%); }
  .foldin { border:1px solid var(--line2); border-top:none;
            border-radius:0 0 12px 12px; padding:4px 12px 12px; }
  .foldin > .card:first-child { margin-top:8px; }
  @media (max-width:560px) {
    /* the tags go under the words rather than squeezing the name */
    .foldbar .tw { flex:1 1 100%; }
    .foldin { padding:4px 8px 10px; }
  }

  /* the ON/OFF switch on the Clubs screen, which owns the rule because
     it owns the control. CHROME is not an f-string -- it is nothing but
     braces -- so it is spliced rather than interpolated. */

  .opt .sw { flex:none; width:42px; height:24px; border-radius:999px;
             background:var(--line2); position:relative; transition:background .15s; }
  .opt .sw i { position:absolute; top:3px; left:3px; width:18px; height:18px;
               border-radius:50%; background:var(--t2); transition:left .15s,
               background .15s; }
  .opt .sw.on { background:var(--go); }
  .opt .sw.on i { left:21px; background:#111; }
  /* a row whose club is put away: the badge and the words go quiet, the
     switch does not, because the switch is the thing to aim at */
  .clubrow.away img, .clubrow.away .tx { opacity:.45; }

  /* a picker tile: a picture and a name, the picture being the point */
  /* ---- the queue at scale: a day heading and a closed row ----
     A queue with a week on it is two dozen cards, each one a title, a
     strip of thumbnails, a caption and five buttons. These two are
     what the rest of it looks like: a heading per day, and one line
     per post that is not asking for anything. */
  .qday { display:flex; align-items:center; gap:10px; margin:20px 0 8px;
          font-family:var(--display); font-weight:800; font-size:15px;
          letter-spacing:.10em; text-transform:uppercase; color:var(--t2); }
  .qday:first-child { margin-top:0; }
  .qday span { font-family:var(--font); font-size:11.5px; font-weight:800;
               letter-spacing:0; color:var(--t3);
               background:rgba(255,255,255,0.06); border-radius:99px;
               padding:1px 8px; }
  .qday.hot { color:var(--warn); }
  .qday.hot span { background:rgba(224,163,37,0.16); color:var(--warn); }
  .qrow { display:flex; align-items:center; gap:12px; cursor:pointer;
          background:var(--card); border:1.5px solid var(--line);
          border-radius:10px; padding:8px 12px; margin-bottom:8px; }
  .qrow:hover { border-color:var(--line2); }
  .qrow img, .qrow .none { width:40px; height:40px; flex:none; border-radius:6px;
                           object-fit:cover; background:#000; }
  .qrow .none { background:rgba(255,255,255,0.05); }
  .qrow .said { min-width:0; flex:1; }
  .qrow .said b { display:block; font-weight:700; font-size:14px; overflow:hidden;
                  text-overflow:ellipsis; white-space:nowrap; }
  .qrow .said span { display:block; font-size:11.5px; color:var(--t3); overflow:hidden;
                     text-overflow:ellipsis; white-space:nowrap; }
  .tiles { display:grid; gap:10px; grid-template-columns:repeat(auto-fill,minmax(148px,1fr)); }
  /* FLEX COLUMN, AND IT IS NOT A STYLE CHOICE. Every screen builds a
     tile as a template literal, so the markup is
     `<button>
  <img>
  <div class=tx>
</button>` and those
     newlines are text nodes. In a BLOCK button they can form a line
     box above the picture -- 8px of it -- and whether they do depends
     on the state of the image, so it happened on two tiles of four and
     not the other two, which read as "black line at the top of two of
     them making the rest out of alignment". Measured, not guessed:
     imgTop was 10, 2, 2, 10 down one row.
     In a flex container a run of whitespace between two elements
     generates no anonymous item at all, by specification, so this is
     the fix rather than trimming the newlines out of a dozen template
     literals and hoping nobody puts one back. */
  .tile { text-align:left; padding:0; overflow:hidden; cursor:pointer;
          background:var(--card); border:2px solid var(--line); border-radius:12px;
          color:var(--t1); display:flex; flex-direction:column; width:100%; }
  .tile.on { border-color:var(--go); }
  .tile img, .tile .ph { display:block; width:100%; aspect-ratio:1; object-fit:cover;
                         background:#000; flex:none; }
  .tile .tx { flex:1; }
  /* THE TILE BEFORE ITS PICTURE ARRIVES, and it has to look like a
     card rather than like a hole. The first call to the examples
     endpoint renders ten cards, which is a few seconds, and a club
     with no photographs never gets a picture for the photo layouts at
     all -- so this is not a flash, it is what the operator is looking
     at while they decide. It was an empty `<img>`, which Chromium
     draws as its broken-image icon on a black square: reported as
     "can we have templates whilst the images load".
     A card-shaped skeleton in the club's own furniture, with the same
     header band and copy block every one of these layouts has, and a
     slow sweep across it so it reads as working rather than failed. */
  .tile .ph { background:var(--raise); display:flex; flex-direction:column;
              justify-content:space-between; padding:0; position:relative;
              overflow:hidden; color:var(--t3); font-size:11.5px; }
  .tile .ph::before { content:""; display:block; height:22%;
                      background:rgba(255,255,255,0.07);
                      border-bottom:2px solid rgba(255,241,1,0.28); }
  .tile .ph::after { content:""; position:absolute; inset:0;
                     background:linear-gradient(100deg,
                       rgba(255,255,255,0) 30%, rgba(255,255,255,0.06) 50%,
                       rgba(255,255,255,0) 70%);
                     background-size:250% 100%;
                     animation:shim 1.5s ease-in-out infinite; }
  .tile .ph i { display:block; font-style:normal; margin:0 12% 26%;
                height:14%; border-radius:3px;
                background:rgba(255,255,255,0.10);
                box-shadow:0 26px 0 -4px rgba(255,255,255,0.06); }
  @keyframes shim { from { background-position:120% 0 } to { background-position:-40% 0 } }
  @media (prefers-reduced-motion: reduce) { .tile .ph::after { animation:none } }
  .tile .tx { padding:8px 10px 10px; }
  .tile .tx b { display:block; font-weight:800; font-size:13.5px; line-height:1.25; }
  .tile .tx span { display:block; font-size:11.5px; line-height:1.35; margin-top:3px;
                   color:var(--t2); }
  .tile.on .tx { background:var(--go); color:#111; }
  .tile.on .tx span { color:rgba(0,0,0,.66); }

  /* Home's "Finish setting up": the one thing on a new club's card that is
     not an everyday action, so it is lit rather than another .opt row */
  .setup-nudge { display:flex; align-items:center; gap:12px; margin-top:16px;
                 padding:12px 14px; border-radius:12px; text-decoration:none;
                 color:var(--t1); background:rgba(255,241,1,0.07);
                 border:1px solid var(--go); }
  .setup-nudge > span:first-child { flex:1; min-width:0; }
  .setup-nudge b { display:block; font-weight:800; font-size:14.5px; }
  .setup-nudge span span { display:block; font-size:12.5px; color:var(--t2); margin-top:2px; }
  .setup-nudge .btn { flex:none; }

  /* an option laid out as a row: an icon or a word, a name, what it is for */
  /* text-decoration:none because `.opt` is a <button> in the pickers and an
     <a> on the home card, and an anchor brings an underline with it: the
     three club actions came out as underlined sentences. */
  .opt { display:flex; align-items:center; gap:12px; width:100%; text-align:left;
         padding:12px 14px; cursor:pointer; border-radius:12px; margin-bottom:6px;
         text-decoration:none;
         background:var(--card); border:1px solid var(--line); color:var(--t1); }
  .opt:hover { border-color:var(--line2); }
  .opt.on { background:rgba(255,241,1,0.07); border-color:var(--go); }
  .opt .ic { flex:none; width:38px; height:38px; border-radius:9px; display:flex;
             align-items:center; justify-content:center; background:rgba(255,255,255,0.05); }
  .opt.on .ic { background:rgba(255,241,1,0.16); }
  .opt .ic svg { width:19px; height:19px; stroke:var(--t2); fill:none; stroke-width:1.7;
                 stroke-linecap:round; stroke-linejoin:round; }
  .opt.on .ic svg { stroke:var(--go); }
  .opt .word { font-size:10.5px; font-weight:800; letter-spacing:1px; text-transform:uppercase;
               color:var(--t3); flex:none; width:78px; }
  .opt .tx { min-width:0; flex:1; }
  .opt .tx b { display:block; font-weight:800; font-size:14.5px; }
  .opt .tx span { display:block; font-size:12.5px; margin-top:2px; color:var(--t2); }
  .opt:disabled { opacity:.5; cursor:default; }
  /* THE ARCHIVE'S MATCH ROWS. A grid and not a flex row of sentences:
     these were four things of varying length laid end to end, so every
     column began somewhere different on every row and the eye had nothing
     to run down. Fixed tracks for the four short things, and the result
     takes what is left, because it is the only one allowed to be long. */
  .opt.arch { display:grid; gap:6px 14px; align-items:baseline;
              grid-template-columns:26px minmax(180px,1.1fr) 92px 78px minmax(0,1.6fr); }
  .arch-ha { font-size:11px; font-weight:900; letter-spacing:.5px; text-align:center;
             border-radius:6px; padding:3px 0; background:rgba(255,255,255,0.07);
             color:var(--t2); }
  /* home and away are the thing a reader scans for, so they are told
     apart by colour and not only by a letter */
  .arch-ha.home { background:rgba(255,241,1,0.16); color:var(--go); }
  .arch-tie { font-size:14px; min-width:0; }
  .arch-day, .arch-comp { font-size:12.5px; color:var(--t2); }
  .arch-res { font-size:12.5px; color:var(--t2); min-width:0; }
  .arch-res i { font-style:normal; color:var(--t3); }
  /* ALREADY POSTED. Quiet on purpose: this is a note on a row the operator
     may still want, not a warning, and a row of loud badges down a list of
     twenty matches would be the loudest thing on a screen whose job is to
     help somebody read the RESULTS. The row itself is dimmed a touch and
     the word rides with the fixture. */
  .arch-done { font-size:11px; font-weight:800; letter-spacing:.4px;
               text-transform:uppercase; color:var(--t3);
               background:rgba(255,255,255,0.07); border-radius:5px;
               padding:2px 6px; white-space:nowrap; }
  .opt.arch.done { opacity:.72; }
  .opt.arch.done.on { opacity:1; }
  /* the same answer on a chip, where there is no room for a word */
  .chip.done { color:var(--t2); }
  .chip.done.on { color:inherit; }
  .hint.done-line b { color:var(--t1); }
  .arch-best { grid-column:2 / -1; font-size:12px; color:var(--t3); }
  .arch-best b { color:var(--t2); font-weight:700; }
  @media (max-width:760px) {
    /* the four short columns go over two lines rather than squeezing the
       result to nothing; the ground keeps its own column on the left */
    .opt.arch { grid-template-columns:26px 1fr auto; }
    .arch-day { text-align:right; }
    .arch-comp, .arch-res { grid-column:2 / -1; }
    .arch-best { grid-column:2 / -1; }
  }
  /* SCROLLED TO, UNDER A STICKY BAR. Anything a screen calls
     `scrollIntoView` on lands at the top of the VIEWPORT, and under 900px
     the top bar is `position:sticky; top:0` and 54px of it, so the thing
     just scrolled to is behind the thing that never moves. Fourth entry in
     this repo about that bar and a different cause each time. Not solved by
     scrolling further: `scroll-margin-top` is the property that means "when
     you scroll me into view, leave this much above me". */
  .jumpto { scroll-margin-top:16px; }
  @media (max-width:900px) { .jumpto { scroll-margin-top:70px; } }
  /* THE ANSWERED ROW. One line, and the word rides beside the name rather
     than in its own 78px column, because a column exists to line up
     against the rows below it and there are none. */
  .opt.tight { padding:9px 12px; }
  .opt.tight .ic { width:32px; height:32px; }
  .opt.tight .ic svg { width:17px; height:17px; }
  .opt.tight .word { width:auto; order:2; margin-left:auto; padding-right:2px; }
  .opt.tight > :last-child { order:3; }
  @media (max-width:560px) {
    /* 78px of a 375px screen, spent on a word the name beside it almost
       always repeats. The hint needs the room more. */
    .opt .word { display:none; }
    .opt.tight .word { display:block; }
    .opt { padding:11px 12px; gap:10px; }
    /* ...AND ON A PHONE THE ANSWERED ROW PUTS THE WORD ABOVE THE NAME. On
       one line there were about 100px left for the name between the word
       and Change, and "Announcement" is wider than that, so it printed
       straight under "NOTICE" (seen on the 375px walk, 27 September 2026).
       Two lines in the same row height: the word as an eyebrow, the name
       under it, the icon and Change spanning both. */
    .opt.tight { display:grid; grid-template-columns:auto minmax(0, 1fr) auto;
                 column-gap:10px; row-gap:1px; }
    .opt.tight .ic { grid-row:1 / span 2; grid-column:1; }
    .opt.tight .word { grid-row:1; grid-column:2; margin:0; padding:0; }
    .opt.tight .tx { grid-row:2; grid-column:2; }
    .opt.tight > :last-child { grid-row:1 / span 2; grid-column:3; }
  }

  /* THE LIGHTBOX. A slide thumbnail is about 150px tall and the card it
     stands for is 1080px, so approving from the thumbnail is approving
     something you have not read. Fit-to-screen first, then a tap for actual
     size and scroll to pan, which is how every photo viewer on a phone
     behaves. `touch-action` lets a pinch through on top of that. */
  #lb { position:fixed; inset:0; z-index:90; display:none;
        background:rgba(6,7,10,0.94); overflow:auto;
        -webkit-overflow-scrolling:touch; touch-action:pinch-zoom; }
  #lb.on { display:block; }
  #lb-inner { min-height:100%; display:flex; align-items:center;
              justify-content:center; padding:56px 12px 12px; }
  #lb img { display:block; border-radius:10px; background:#000;
            max-width:100%; height:auto; cursor:zoom-in; }
  #lb.full #lb-inner { align-items:flex-start; justify-content:flex-start; }
  #lb.full img { max-width:none; width:auto; cursor:zoom-out; }
  #lb-bar { position:fixed; top:0; left:0; right:0; height:48px; z-index:91;
            display:flex; align-items:center; gap:8px; padding:0 12px;
            background:rgba(6,7,10,0.92); }
  #lb-bar .sp { margin-left:auto; }
  #lb-cap { color:var(--t2); font-size:12.5px; overflow:hidden;
            white-space:nowrap; text-overflow:ellipsis; }
  img[data-zoom] { cursor:zoom-in; }
  /* THROUGH THE SLIDES WITHOUT CLOSING (17 September 2026). A carousel is
     up to ten cards and the lightbox showed one, so reading a post meant
     close, scroll the strip, open the next. The arrows are for a mouse and
     sit outside the card; the swipe is for the phone, where there is
     nothing to aim at. They are hidden on a post with one slide, because an
     arrow that goes nowhere is a control that has stopped meaning anything.
     Hidden at actual size too: there the drag is panning the card. */
  #lb-prev, #lb-next { position:fixed; top:50%; transform:translateY(-50%);
            z-index:91; width:46px; height:64px; display:none;
            align-items:center; justify-content:center; border:none;
            border-radius:10px; background:rgba(6,7,10,0.72); color:#fff;
            font-size:26px; line-height:1; cursor:pointer; }
  #lb-prev { left:6px; } #lb-next { right:6px; }
  #lb.many:not(.full) #lb-prev, #lb.many:not(.full) #lb-next { display:flex; }
  #lb-of { color:var(--t3); font-size:12.5px; flex:none; }

  /* A QUEUE ROW'S BUTTONS ON A PHONE. Up to eight of them at full size
     wrapped onto three and four lines, so the one that matters sat among
     the ones that rarely do. The first action takes the whole width, and
     everything after it is compact enough for two lines. Hit areas stay
     over 36px tall */
  @media (max-width:560px) {
    .row-acts { gap:6px; }
    .row-acts > .btn:first-child { flex:1 1 100%; }
    .row-acts > .btn:not(:first-child) { padding:9px 12px; font-size:13px; }
  }
  /* A SQUAD ROW ON A PHONE: three buttons beside the name left it one
     word a line ("A / Singh / Dhinsa"), so under 560px they go under it,
     lined up with the name rather than the face */
  @media (max-width:560px) {
    .sq-row { flex-wrap:wrap; }
    .sq-acts { flex-basis:100%; padding-left:61px; }
  }
  /* A SCHEDULE ROW. Under 560px the tag drops onto its own line and the
     title gets the room, which is why this is a class and not inline. */
  .srow { display:flex; gap:13px; align-items:center; background:var(--card);
          border:1px solid var(--line); border-radius:12px;
          padding:11px 14px; margin-bottom:9px; text-decoration:none; }
  .srow .when { font-size:13px; color:var(--t2); width:52px; flex:none; }
  .srow .art { width:52px; height:52px; flex:none; border-radius:8px;
               object-fit:cover; background:rgba(255,255,255,0.04);
               border:1px solid var(--line); }
  .srow .gap { width:52px; height:52px; flex:none; border-radius:8px;
               background:rgba(255,255,255,0.04); display:flex; align-items:center;
               justify-content:center; color:var(--t3); font-size:10px;
               text-align:center; line-height:1.2; padding:2px; }
  .srow .said { flex:1; min-width:0; }
  .srow .said b { display:block; font-weight:700; overflow:hidden;
                  text-overflow:ellipsis; white-space:nowrap; }
  .srow .said span { display:block; color:var(--t2); font-size:12.5px; margin-top:3px;
                     overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  @media (max-width:560px) {
    .srow { flex-wrap:wrap; }
    .srow .said { flex:1 1 100%; order:3; min-width:0; }
    .srow .tag { order:4; }
  }
  /* a row that will not run (off, out of season): one quiet line, even on
     a phone, and the club's name beside the kind rather than under it */
  .srow.slim { padding:8px 14px; margin-bottom:6px; opacity:.6; flex-wrap:nowrap; }
  .srow.slim .said { display:flex; gap:8px; align-items:baseline; order:0; flex:1; }
  .srow.slim .said b { font-weight:600; font-size:13.5px; flex:none; overflow:visible; }
  .srow.slim .said span { margin-top:0; }
  .srow.slim .tag { order:0; }

  /* A QUEUE ROW'S HEADING ON A PHONE (17 September 2026). The state tag and
     the title shared one line, and "WAITING FOR YOU" is the longest state
     there is -- about 130px of a 375px screen, before the gap -- so the
     title it was sitting beside had a third of the row to say what the post
     was, and every season post read "Season review..." and stopped.
     Reported as "the waiting for you is huge, so you can't see a lot of the
     title". On a phone they are two rows: the tag first, because a tap
     decision starts with what state the post is in, and then the title with
     the whole width. Wider than that, nothing changes. */
  .qhead { display:flex; justify-content:space-between; gap:12px;
           align-items:flex-start; flex-wrap:wrap; }
  .qhead .said { min-width:0; }
  @media (max-width:560px) {
    .qhead { flex-direction:column-reverse; align-items:flex-start; gap:8px; }
    .qhead .said { width:100%; }
  }

  /* WORK HAPPENING, 18 September 2026. Run a job's state panel said
     "Cup stats - running..." inside a plain bordered box, which reads as
     the status of a queue row rather than as a job under way -- and a
     season post is one GET per match, so it sat like that for minutes.
     Reported as "this gets confusing, it is like the status of the queue".
     A moving bar, the stage in words, and how long it has been going. */
  .card.working { border-color:rgba(255,241,1,0.30); }
  .work-bar { height:4px; border-radius:3px; overflow:hidden; margin:0 0 13px;
              background:rgba(255,255,255,0.08); }
  .work-bar i { display:block; height:100%; width:38%; border-radius:3px;
                background:var(--go); animation:runs 1.4s ease-in-out infinite; }
  @keyframes runs { from { margin-left:-40% } to { margin-left:102% } }
  @media (prefers-reduced-motion: reduce) {
    .work-bar i { animation:none; width:100%; }
  }
  .work-hd { font-weight:800; font-size:15px; }
  .work-ago { font-size:12px; color:var(--t3); margin-top:12px; }

  /* CONNECT INSTAGRAM (SaaS Session 7): the same card on Set up and in
     Settings, drawn by static/js/instagram.js */
  .ig-how { padding-left:20px; margin:4px 0 10px; font-size:14px; line-height:1.5; color:var(--t2); }
  .ig-how li { margin-bottom:6px; }
  .ig-on { font-size:14.5px; line-height:1.5; }
  .ig-row { margin-top:12px; }
  .ig-row + .hint { margin-top:8px; }
