

:root{

  --bg:#F2F1F8; --card:#FFFFFF; --ink:#221F2B; --soft:#6C6784; --line:#E6E3F0;

  --plum:#2B1F42; --plum2:#5B3383; --accent:#6C4AB6; --accent-soft:#F0EAFB;

  --s1:#EFB423; --s2:#2FA96B; --s3:#0E9FB5; --s4:#4A7FE0; --s5:#7B5BD6; --s6:#C0559A; --s0:#A9A4B6;

  --good:#1E8F5C; --bad:#CE4B58;

  --display:"Bricolage Grotesque",system-ui,-apple-system,"Segoe UI",sans-serif;

  --sans:"DM Sans",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,sans-serif;

  --mono:"DM Mono",ui-monospace,"SF Mono",Menlo,Consolas,monospace;

  --sh:0 1px 2px rgba(34,31,43,.05),0 10px 28px -16px rgba(34,31,43,.28);

  background:var(--bg);color:var(--ink);font-family:var(--sans);

  min-height:100%;padding:0 0 44px;box-sizing:border-box;

}

#app *{box-sizing:border-box}

/* The hidden attribute works through a low-specificity UA rule, so any

   id-level display rule below (the wide-screen grids) would silently beat it

   and leave every tab on screen at once. */

#app [hidden]{display:none!important}

.wrap{max-width:640px;margin:0 auto;padding:0 14px}

/* ---------- hero ---------- */

.hero{background:linear-gradient(145deg,var(--plum) 0%,var(--plum2) 100%);

  border-radius:0 0 22px 22px;margin:0 -14px 16px;padding:24px 22px 0;color:#fff;

  box-shadow:0 14px 34px -22px rgba(43,31,66,.9);overflow:hidden}

.herotop{display:flex;align-items:flex-start;justify-content:space-between;gap:14px}

.brand{font-family:var(--display);font-weight:800;font-size:32px;line-height:.95;

  letter-spacing:-.035em;margin:0}

.herosub{font-family:var(--mono);font-size:11.5px;color:rgba(255,255,255,.62);margin:9px 0 0;letter-spacing:.02em}

.weave{display:flex;flex-direction:column;gap:3px;margin:20px -22px 0;padding:0 0 4px}

.wrib{display:flex;height:7px;opacity:.92}

.wrib i{flex:1;display:block;animation:slide .5s cubic-bezier(.2,.8,.3,1) both}

@keyframes slide{from{transform:scaleX(0);opacity:0}to{transform:scaleX(1);opacity:1}}

.chip.me{background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.2);color:#fff;

  display:flex;align-items:center;gap:8px;padding:6px 13px 6px 7px;border-radius:999px;

  font-size:13px;font-weight:500;flex:none}

.chip.me:hover{background:rgba(255,255,255,.22)}

/* ---------- cards ---------- */

.card{background:var(--card);border-radius:18px;padding:18px;margin-bottom:12px;box-shadow:var(--sh)}

.todaycard{position:relative;overflow:hidden;background:linear-gradient(145deg,#fff 15%,#F4EDFF 100%);border:1px solid rgba(108,74,182,.12);border-radius:22px;padding:20px;margin-bottom:12px;box-shadow:0 16px 38px -25px rgba(43,31,66,.65)}

.todaycard::after{content:"";position:absolute;width:150px;height:150px;border-radius:50%;right:-70px;top:-80px;background:rgba(108,74,182,.08)}

.todaytop{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.todaytop h2{font-family:var(--display);font-size:25px;line-height:1;margin:0;letter-spacing:-.03em}

.sharebtn{position:relative;z-index:1;background:var(--ink);color:#fff;border:0;padding:9px 12px;font-size:12px;font-weight:700}

.todaygrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:16px}.pulse{display:flex;gap:9px;align-items:center;background:rgba(255,255,255,.72);border:1px solid rgba(108,74,182,.1);border-radius:13px;padding:10px}.pulse .ico{font-size:20px}.pulse .dubav{display:flex;flex:none;align-items:center}.pulse .dubav .av{margin-right:-8px;box-shadow:0 0 0 2px #fff}.pulse .dubav .av:last-child{margin-right:0}.pulse b,.pulse small{display:block}.pulse b{font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pulse small{font-size:10.5px;color:var(--soft);margin-top:2px}

.commentary{font-family:var(--display);font-size:16px;line-height:1.45;margin-top:14px;padding:13px 14px;background:var(--plum);color:#fff;border-radius:14px}.commentary:empty{display:none}

.awards{display:flex;gap:7px;overflow-x:auto;margin-top:12px;padding-bottom:2px}.award{flex:0 0 auto;background:#fff;border:1px solid var(--line);border-radius:12px;padding:8px 10px;max-width:180px}.award b,.award small{display:block}.award b{font-size:11.5px}.award small{font-size:10.5px;color:var(--soft);margin-top:2px}

.rivalduo{display:grid;grid-template-columns:1fr 1fr;gap:9px}.rivalbox{border:1px solid var(--line);border-radius:14px;padding:13px;background:#FAF9FD}.rivalbox .face{font-size:20px}.rivalbox b,.rivalbox small{display:block}.rivalbox b{margin-top:6px}.rivalbox small{color:var(--soft);font-size:11.5px;margin-top:3px}.rivalnote{font-size:12px;color:var(--soft);margin:12px 0 0}

.btype{border:1px solid var(--line);border-radius:14px;margin-bottom:8px;overflow:hidden;background:#fff}

.btype.open{border-color:var(--accent);box-shadow:0 6px 20px -14px var(--accent)}

.btop{display:flex;align-items:center;gap:12px;width:100%;background:transparent;border:0;

  border-radius:0;padding:13px;text-align:left;color:var(--ink)}

.btop:hover{background:#FAF8FE}

.bico{font-size:26px;flex:none;line-height:1}

.bmain{flex:1;min-width:0}

.bmain b{display:block;font-size:14.5px}

.bmain small{display:block;font-size:11.5px;color:var(--soft);margin-top:2px}

.blead{font-family:var(--mono);font-size:12px;color:var(--soft);text-align:right;flex:none;max-width:44%}

.bchev{color:var(--soft);font-size:17px;flex:none;transition:transform .18s}

.btype.open .bchev{transform:rotate(90deg)}

.blist{padding:2px 13px 11px}

.brow{display:flex;align-items:center;gap:10px;padding:8px 0;border-top:1px solid var(--line)}

.brow:first-child{border-top:0}

.bnum{font-family:var(--mono);font-size:15px;flex:none}

.badgegrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px}.badge{display:flex;align-items:center;gap:10px;border:1px solid var(--line);border-radius:14px;padding:11px;background:linear-gradient(145deg,#fff,#F8F5FD)}.badge .medal{font-size:26px}.badge b,.badge small{display:block}.badge b{font-size:12px}.badge small{font-size:10.5px;color:var(--soft);margin-top:2px}

.drawtitle{font-family:var(--display);font-size:18px;margin:2px 0 10px}.drawpair{animation:drawIn .7s cubic-bezier(.2,.85,.3,1) both}.drawpair:nth-child(2){animation-delay:.15s}.drawpair:nth-child(3){animation-delay:.3s}@keyframes drawIn{from{opacity:0;transform:translateY(18px) scale(.96)}to{opacity:1;transform:none}}

.celebrate{position:fixed;inset:0;z-index:999;display:flex;align-items:center;justify-content:center;background:rgba(24,16,38,.72);backdrop-filter:blur(5px);animation:fadein .25s both}.celebrate[hidden]{display:none}.champion{position:relative;text-align:center;background:linear-gradient(145deg,#fff,#FFF7D8);padding:34px 30px;border-radius:26px;width:min(88vw,390px);box-shadow:0 25px 80px rgba(0,0,0,.35);animation:champ .65s cubic-bezier(.2,.9,.25,1.2) both}.champion .cup{font-size:58px}.champion h2{font-family:var(--display);font-size:28px;margin:8px 0 4px}.champion p{margin:5px 0;color:var(--soft)}.champion .champdone{display:inline-block;margin-top:18px;background:var(--accent)!important;color:#fff!important;border:0!important}.champion .champclose{position:absolute;right:12px;top:10px;margin:0;width:40px;height:40px;padding:0;border-radius:50%;font-size:24px;line-height:1;background:#EEE9F5;color:var(--ink);border:0}.confetti{position:absolute;inset:0;overflow:hidden;pointer-events:none}.confetti i{position:absolute;top:-12px;width:9px;height:18px;animation:fall 2s ease-in forwards}@keyframes fadein{from{opacity:0}}@keyframes champ{from{opacity:0;transform:scale(.7) rotate(-2deg)}}@keyframes fall{to{transform:translateY(105vh) rotate(700deg)}}

.accentcard{background:linear-gradient(160deg,#fff 60%,var(--accent-soft))}

.cardhead{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:13px}

.eyebrow{font-family:var(--mono);font-size:10.5px;letter-spacing:.15em;text-transform:uppercase;

  color:var(--accent);margin:0 0 11px;font-weight:500}

.cardhead .eyebrow{margin:0}

.h2{font-family:var(--display);font-size:24px;font-weight:800;letter-spacing:-.03em;margin:0 0 7px}

.note{font-size:13.5px;line-height:1.55;color:var(--soft);margin:0 0 15px}

.row{display:flex;gap:8px;align-items:center}

.input,.ta{flex:1;min-width:0;background:#fff;color:var(--ink);border:1.5px solid var(--line);

  border-radius:12px;padding:12px 13px;font-family:var(--sans);font-size:15px}

.ta{width:100%;font-family:var(--mono);font-size:13px;line-height:1.55;resize:vertical;margin-bottom:11px;display:block}
/* Safari on iOS zooms the whole page in whenever a focused field's text is
   under 16px, which reads as the card swelling as you tap into it. Raising the
   size on touch screens is the only fix that does not also take pinch-zoom
   away from everyone, which maximum-scale in the viewport tag would. */
@media (pointer:coarse){.ta,.input{font-size:16px}}

.input:focus,.ta:focus{outline:0;border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}

button{font-family:var(--sans);cursor:pointer;border-radius:11px;font-size:14px;
  transition:transform .15s cubic-bezier(.34,1.56,.64,1),background .15s}
/* A small tactile press, everywhere, for free - the transform transition
   already existed on every button, this just gives it something to animate
   to. Skipped on toggles mid-flip and disabled buttons, where a press-scale
   would fight the state change already happening. */


button:active:not(:disabled){transform:translateY(1px) scale(.97)}

.primary{background:var(--accent);color:#fff;border:0;padding:12px 20px;font-weight:700;white-space:nowrap;

  box-shadow:0 6px 16px -8px var(--accent)}

.primary:hover{background:#5B3AA5}

.primary:disabled{background:#E3E0EC;color:#A8A3B8;box-shadow:none;cursor:default}

.ghost{background:#fff;color:var(--soft);border:1.5px solid var(--line);padding:8px 13px;font-size:12.5px;font-weight:500}

.ghost:hover{color:var(--ink);border-color:#CFCADF}

.ghost.sq{width:34px;padding:8px 0;text-align:center}

.ghost.danger:hover{color:var(--bad);border-color:#F0C3C7}
/* Three sections have carried these class names with no rule behind them,
   rendering as plain cards despite implying something distinct. */
.explain{background:var(--accent-soft);border-radius:18px}
.timecard{background:linear-gradient(150deg,#fff 55%,#F3EFFB);border:1px solid rgba(108,74,182,.1)}
.rivalry{background:linear-gradient(150deg,#fff 55%,#FBF3EF)}
/* A quiet invitation until switched on, then unmistakably active - the same
   visual language the segmented controls already use for "on". */
/* Gold rather than the purple accent: this toggle sits inside a mostly-purple
   app and needs to read as its own thing, not as "another primary action".
   #A66D08 rather than the ribbon's --s1 gold - that lighter gold fails white
   text contrast (1.9:1); this one clears WCAG AA (4.4:1). */
.accentbtn{background:#FCF1DA;color:#8A5B08;border:1.5px solid transparent;
  font-weight:700;padding:8px 13px;font-size:12.5px}
.accentbtn:hover{background:#A66D08;color:#fff}
.accentbtn.on{background:#A66D08;color:#fff;box-shadow:0 6px 16px -9px rgba(166,109,8,.55)}
.ftbtns{display:flex;gap:8px}
.moremenu{display:flex;flex-direction:column;margin:4px 14px 8px;border-top:1px solid var(--line);
  animation:fadein .15s ease both}
.moremenu button{width:100%;justify-content:flex-start;text-align:left;border-radius:0;
  border-left:0;border-right:0;border-top:0;border-bottom:1px solid var(--line);background:transparent}
.moremenu button:last-child{border-bottom:0}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* The completed-week headline: three short lines, no clutter, above the table
   it summarises rather than requiring the reader to work it out row by row. */
.worp{background:var(--accent-soft);border-radius:14px;padding:11px 14px;margin-bottom:12px;
  font-size:13px;line-height:1.6}
.worp p{margin:0}
.worp b{font-weight:700}

button:focus-visible,.input:focus-visible{outline:2.5px solid var(--accent);outline-offset:2px}

.nav{display:flex;gap:6px}

.preview{flex:1;min-width:0;font-family:var(--mono);font-size:11.5px;color:var(--soft);

  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* A valid paste reads as an actionable chip leading into Add, not a status
   line you might not notice. Detection logic is unchanged - this only styles
   the outcome differently once it's genuinely ready to submit. */
.preview.ok{color:var(--good);background:#E9F7EF;border-radius:999px;padding:5px 11px;
  font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.preview.bad{color:var(--bad)}
.addactions{align-items:center;flex-wrap:wrap}
.addactions .preview{flex:1 1 100%}
.addactions #pasteAddBtn,.addactions #addBtn{flex:1 1 0;min-width:0;white-space:nowrap}
@media(min-width:560px){
  .addactions{flex-wrap:nowrap}
  .addactions .preview{flex:1 1 auto}
  .addactions #pasteAddBtn,.addactions #addBtn{flex:0 0 auto}
}

.tabs{display:flex;gap:5px;margin-bottom:13px;overflow-x:auto;padding-bottom:3px}

.tabs button{background:#fff;border:0;color:var(--soft);padding:9px 15px;font-size:13px;

  white-space:nowrap;flex:none;font-weight:500;box-shadow:var(--sh);border-radius:999px}

.tabs button.on{background:var(--ink);color:#fff}

.seg{display:flex;background:#F1EFF7;border-radius:11px;padding:3px;flex:none;gap:2px}

.seg.wide{margin-bottom:15px}

.seg button{background:transparent;border:0;border-radius:8px;color:var(--soft);

  padding:7px 9px;font-size:11.5px;font-family:var(--sans);font-weight:500;flex:1;white-space:nowrap}

.seg button.on{background:#fff;color:var(--ink);box-shadow:0 1px 3px rgba(34,31,43,.12)}

/* ---------- avatars ---------- */

.av{display:inline-flex;align-items:center;justify-content:center;flex:none;

  border-radius:9px;background:#F1EFF7;overflow:hidden;vertical-align:middle;line-height:1}

.av.ini{font-family:var(--mono);font-weight:500;color:var(--soft)}

.av .sg{display:grid;grid-template-columns:repeat(3,1fr);width:100%;height:100%}

.av .sg i{display:block}

.avpreview{display:flex;align-items:center;gap:15px;margin-bottom:17px}

.avpreview b{display:block;font-family:var(--display);font-size:20px;font-weight:800;letter-spacing:-.02em}

.avpreview small{display:block;font-size:12.5px;color:var(--soft);margin-top:3px}

.epick{display:grid;grid-template-columns:repeat(auto-fill,minmax(44px,1fr));gap:7px;max-height:216px;overflow-y:auto}

.epick button{background:#F6F4FB;border:2px solid transparent;height:44px;font-size:22px;padding:0;line-height:1;border-radius:12px}

.epick button.on{border-color:var(--accent);background:var(--accent-soft)}

.sigwrap{display:flex;gap:18px;align-items:flex-start;flex-wrap:wrap}

.sigedit{display:grid;grid-template-columns:repeat(3,46px);grid-template-rows:repeat(3,46px);gap:5px}

.sigedit button{padding:0;border:0;border-radius:8px}

.paints{display:grid;grid-template-columns:repeat(4,38px);gap:7px}

.paints button{width:38px;height:38px;padding:0;border:3px solid transparent;border-radius:11px}

.paints button.on{border-color:var(--ink)}

.copyout{flex:1;min-width:0;font-family:var(--mono);font-size:15px;background:#F6F4FB;

  border:1.5px solid var(--line);border-radius:12px;padding:10px 12px;line-height:1.35;white-space:pre;overflow-x:auto}

/* ---------- week board ---------- */

.wrow{display:grid;grid-template-columns:1fr auto;gap:6px 10px;align-items:center;

  padding:11px 0;border-top:1px solid var(--line)}

.wrow:first-child{border-top:0}

.wname{grid-column:1/-1;display:flex;align-items:center;gap:8px;min-width:0;font-weight:700;font-size:14.5px}

.wnm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}

.wrow.win .wnm{color:var(--accent)}

.wcells{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:4px;min-width:0}

.wcell{display:flex;flex-direction:column;align-items:center;gap:3px;min-width:0}

.wday{font-family:var(--mono);font-size:9px;letter-spacing:.04em;color:var(--soft);text-transform:uppercase}
.wdaynum{display:none}
/* Puzzle numbers stay off by default - the day letters are enough on the main
   table - but the per-player detail panel wants them, since "what puzzle was
   this" is the whole point of opening it. */
.myweekcard .wdaynum{display:block;font-family:var(--mono);font-size:7.5px;color:var(--soft);opacity:.7}
.wrow[data-slug]{cursor:pointer}
.wrow[data-slug]:hover{background:rgba(108,74,182,.04)}

/* min-width AND max-width together: without an upper bound, the caption text

   below the total sizes this auto grid column to its own unwrapped width. The

   "index -X.XX that week" addition made that text long enough to push the

   whole column wide enough to overlap the day cells to its left. Capped and

   allowed to wrap instead. */

.wtot{display:flex;flex-direction:column;align-items:flex-end;justify-content:center;flex:none;min-width:46px;max-width:108px}

.wtot b{font-family:var(--mono);font-size:17px;line-height:1.1}

.wtot small{font-family:var(--mono);font-size:10.5px;color:var(--soft);white-space:normal;text-align:right;line-height:1.35;display:block}

.hcol{display:flex;flex-direction:column;align-items:center;gap:3px}

.hcol span{font-family:var(--mono);font-size:9.5px;letter-spacing:.04em;color:var(--soft)}

.hini{font-family:var(--mono);font-size:12px;letter-spacing:.06em}

/* ---------- head to head grid ---------- */

.tablewrap{overflow-x:auto;margin:0 -18px;padding:0 18px}

table.wk{border-collapse:separate;border-spacing:0 4px;font-family:var(--mono);font-size:12.5px;width:100%}

table.wk th{font-weight:500;color:var(--soft);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;

  padding:0 0 6px;text-align:center;font-family:var(--sans)}

table.wk th.nm,table.wk td.nm{text-align:left;position:sticky;left:0;background:#fff;z-index:2;

  padding-right:10px;font-family:var(--sans);font-size:13px;font-weight:700;white-space:nowrap;

  box-shadow:6px 0 8px -8px rgba(34,31,43,.35)}

table.wk td.nm .wnm{display:inline-block;max-width:82px;overflow:hidden;text-overflow:ellipsis;

  white-space:nowrap;vertical-align:middle}

@media (min-width:560px){table.wk td.nm .wnm{max-width:none}}

.nmin{display:flex;align-items:center;gap:8px}

/* Responsive H2H: keep the full matrix on wider screens, but use a selected-player
   vertical list on phones so full names remain readable as the roster grows. */
.h2hmobile{display:none}
.h2hchoose{display:block;margin-bottom:12px}
.h2hchoose>span{display:block;font-size:11px;font-weight:700;color:var(--soft);margin:0 0 5px}
.h2hmobilelist{display:grid;gap:8px}
.h2hopp{display:flex;align-items:center;gap:10px;border:1px solid var(--line);border-radius:13px;padding:10px 11px;background:#FAF9FD}
.h2hopp .v{flex:1;min-width:0}
.h2hopp .v b,.h2hopp .v small{display:block}
.h2hopp .v b{font-size:13.5px}
.h2hopp .v small{font-size:10.5px;color:var(--soft);margin-top:2px}
.h2hscore{font-family:var(--mono);font-weight:700;font-size:15px;white-space:nowrap}
.h2hscore.up{color:var(--good)}.h2hscore.dn{color:var(--bad)}.h2hscore.lvl{color:var(--soft)}
.h2hmobilekey{display:none}
@media(max-width:559px){
  #h2hMatrix{display:none}
  .h2hmobile{display:block}
  .h2hdesktopkey{display:none}
  .h2hmobilekey{display:inline}
}

table.wk td{padding:2px;text-align:center}

table.wk tr.win td.nm{color:var(--accent)}

.cell{position:relative;display:block;width:30px;height:30px;line-height:30px;border-radius:9px;margin:0 auto;

  color:#fff;font-weight:500;font-size:13px;animation:pop .3s cubic-bezier(.2,.9,.3,1.2) both}

.cell.na{background:#F4F2F9;color:#C6C1D6;box-shadow:none}

  .cell.fail{

  background:#C62828 !important;

}

  .cell.fail::before{

  content:"😠";

  position:absolute;

  left:50%;

  top:50%;

  transform:translate(-50%,-50%) scale(1.15);

  font-size:25px;

  line-height:1;

  opacity:.95;

  z-index:0;

}

  .cell.fail .failnum{

  position:relative;

  z-index:2;

}

  .cell.miss{

  background:#FFF0DE !important;

  color:#5B5866;

  overflow:hidden;

  font-weight:800;

}

.cell.miss::before{

  content:"👻";

  position:absolute;

  left:50%;

  top:50%;

  transform:translate(-50%,-50%) scale(1.15);

  font-size:25px;

  line-height:1;

  filter:grayscale(1);

  opacity:.72;

  z-index:0;

}

.cell.miss .missnum{

  position:relative;

  z-index:2;

  color:#55515F;

}

/* A first-guess solve keeps the gold score colour, then celebrates it with a
   multicolour radial burst. The class is assigned while rendering, so it also
   applies retrospectively to every stored 1/6. */
.cell.one{
  background:#EFB423 !important;
  color:#3D2900 !important;
  overflow:hidden;
  font-weight:900;
  isolation:isolate;
  text-shadow:0 1px 0 rgba(255,255,255,.7),0 0 3px rgba(255,255,255,.9);
}
.cell.one::before{
  content:"";
  position:absolute;
  inset:-45%;
  z-index:0;
  background:repeating-conic-gradient(from -8deg,
    #ff2f92 0 8deg,#ffd62e 8deg 16deg,#20d7d0 16deg 24deg,
    #7b5bd6 24deg 32deg,#ff7043 32deg 40deg,#49c96d 40deg 48deg);
  opacity:.9;
  transform:rotate(8deg);
}
.cell.one::after{
  content:"";
  position:absolute;
  inset:4px;
  z-index:1;
  border-radius:50%;
  background:radial-gradient(circle,#FFE98A 0 35%,rgba(239,180,35,.88) 58%,rgba(239,180,35,0) 72%);
}
.cell.one .onenum{position:relative;z-index:2}

.cell.dropped{opacity:.3}

.cell.best{box-shadow:0 0 0 2.5px var(--ink)}

.cell.sog::after{content:"";position:absolute;top:3px;right:3px;width:6px;height:6px;border-radius:50%;

  background:#fff;box-shadow:0 0 0 1.5px rgba(0,0,0,.14)}

@keyframes pop{from{transform:scale(.5);opacity:0}to{transform:scale(1);opacity:1}}

td.tot{font-weight:500;font-size:15px;padding-left:11px}

td.par{font-size:11.5px;padding-left:8px}

.up{color:var(--good)} .dn{color:var(--bad)} .lvl{color:var(--soft)}

.tag{font-family:var(--sans);font-size:9px;font-weight:700;letter-spacing:.06em;padding:2px 6px;

  border-radius:999px;margin-left:6px;vertical-align:middle}

.tag.w{background:var(--accent-soft);color:var(--accent)}

.tag.s{background:#FCEBED;color:var(--bad)}

/* ---------- Time Machine ---------- */
.timeico{font-size:34px;line-height:1}
.timecontrols{display:grid;grid-template-columns:1fr;gap:10px}
.timecontrols label{display:block;font-size:11px;font-weight:700;color:var(--soft)}
.timecontrols label span{display:block;margin:0 0 5px}
.timecontrols .input{width:100%;box-sizing:border-box}
.timeactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.timeactions button{flex:1 1 145px}
#timeStatus{min-height:18px;margin:11px 0 0;color:var(--soft)}
.tmhead{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:13px}
.tmanswer{font-family:var(--display);font-size:28px;letter-spacing:.12em;margin:2px 0 0;text-transform:uppercase}
.tmmeta{color:var(--soft);font-size:12px;margin:5px 0 0}
.tmplayers{display:grid;gap:10px}
.tmplayer{border:1px solid var(--line);border-radius:15px;padding:12px;background:#FAF9FD}
.tmplayerhead{display:flex;align-items:center;gap:9px}
.tmplayerhead b{flex:1;min-width:0}
.tmplayerhead .cell{flex:none;margin:0}
.tmgrid{font-size:19px;line-height:1.18;letter-spacing:1px;margin:10px 0 0;white-space:pre-line}
.tmnogrid{font-size:11px;color:var(--soft);margin:8px 0 0}
.explain summary{cursor:pointer;font-weight:800;color:var(--accent)}
.explain p{font-size:13px;line-height:1.55;color:var(--soft);margin:11px 0 0}
.explain p b{color:var(--ink)}

@media (min-width:560px){
  .timecontrols{grid-template-columns:1fr 1fr}
  .tmplayers{grid-template-columns:repeat(2,minmax(0,1fr))}
}

.tag.b{background:#FFF4D9;color:#9A6B00}
.bonuschip{font-family:var(--mono);font-size:10.5px;font-weight:700;color:#9A6B00;
  margin-left:5px;vertical-align:middle}
.mbar{display:flex;height:6px;border-radius:4px;overflow:hidden;background:var(--line);
  margin:6px 0 4px;width:100%}
.mbar .mw{background:var(--good)}
.mbar .md{background:var(--s0)}
.mbar .ml{background:var(--bad)}
.detailrange{font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--soft);margin:-6px 0 12px}
.seg.inert{opacity:.4;pointer-events:none}
.rangenote{display:block;font-size:11px;color:var(--soft);margin:-4px 0 8px}
.ribbon i{cursor:pointer}
.hcompare{display:flex;align-items:center;justify-content:center;gap:4px;width:100%;height:100%;
  font-family:var(--mono);font-size:11px;font-weight:700;color:var(--ink);background:var(--accent-soft)}
.hcompare .hcarrow{color:var(--soft);font-size:10px}
.hchead{text-align:right;font-size:11px;color:var(--soft);margin:-2px 0 6px;font-weight:600}
.hchead .hcarrow{color:var(--soft)}
.hcgrid{display:grid;grid-template-columns:repeat(5,1fr);gap:5px;margin-top:8px}
.hcd{text-align:center;padding:6px 2px;border-radius:8px;background:#F6F4FB;
  font-family:var(--mono);font-size:11px;font-weight:700;color:var(--soft)}
.hcd.best{background:var(--accent-soft);color:var(--accent);box-shadow:inset 0 0 0 1.5px var(--accent)}
.myweekcard .wtot{min-width:60px}
.wword{font-family:var(--display);font-size:15px;font-weight:800;
  letter-spacing:.08em;color:var(--accent)}
.tmtoday{font-size:10px;color:var(--soft);font-weight:600;text-align:right}
/* A quiet distinguishing tint, matching the treatment Record Book, Roll of
   Honour and Opening Words already got, so this stops reading as identical
   to every other plain card on the page. */
.card.dubcard{background:linear-gradient(150deg,#fff 55%,#EFF7F1)}
.pair:hover{background:rgba(108,74,182,.04)}
.dubmember{margin-bottom:12px}
.dubmemberhead{display:flex;align-items:center;gap:9px;font-size:14px;margin-bottom:6px}
.dubmemberhead b{flex:1}
.dubmemberhead .dubtotal{font-family:var(--mono);font-weight:700}
.aubpanel{margin-top:14px;padding:13px;border:1px solid #E8D9B7;border-radius:14px;background:linear-gradient(145deg,#FFFBF2,#FFF7E2)}
.dubview{margin:8px 0 12px;max-width:330px}.aubrecap{margin-top:13px;padding:11px 12px;border-radius:12px;background:#F3EFFB;border:1px solid #E2D8F2;font-size:12px;line-height:1.5}.aubrecap>b{font-family:var(--display);font-size:14px}.aubrecap p{margin:4px 0 0;color:var(--soft)}
.aubhead{display:flex;justify-content:space-between;align-items:flex-start;gap:10px;margin-bottom:10px}.aubhead b,.aubhead small{display:block}.aubhead small{font-size:11px;color:var(--soft);margin-top:2px}.aubclock{font-family:var(--mono);font-size:9.5px;color:#8A5B08;white-space:nowrap}
.aubholds{display:grid;gap:6px;margin:8px 0 10px}.aublabel{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--soft);margin-right:7px}.aubchip{display:inline-block;font-size:11px;font-weight:700;background:#fff;border:1px solid #E8D9B7;border-radius:999px;padding:4px 8px;margin:2px 4px 2px 0}.aubchip.quiet{font-weight:500}.aubnone{font-size:11px;color:var(--soft)}
.aubbuy,.aubplay{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-top:9px}.aubact{padding:6px 9px;font-size:11px}.aubpledge{margin-top:9px;padding:9px 10px;border-radius:10px;background:#fff;border:1px solid #E8D9B7;font-size:11.5px;display:flex;gap:8px;align-items:center;justify-content:space-between}.aubpledge .primary{padding:7px 9px;font-size:11px}.aubalert{margin:9px 0 0;padding:8px 10px;border-radius:10px;background:#FCEBED;color:var(--bad);font-size:11.5px}.aubrules{margin-top:10px;font-size:11.5px;color:var(--soft)}.aubrules summary{cursor:pointer;font-weight:700;color:#8A5B08}.aubrules p{line-height:1.5}.dday .aubspark{font-style:normal;color:#A66D08;margin-left:2px}.dprow .aubdaynote{grid-column:2/-1;font-size:9.5px;color:#8A5B08;display:block;margin-top:2px}
.aubmodal{position:fixed;inset:0;z-index:1100;display:flex;align-items:center;justify-content:center;padding:18px;background:rgba(24,16,38,.7);backdrop-filter:blur(4px)}.aubmodal[hidden]{display:none}.aubmodalbox{width:min(92vw,390px);background:#fff;border-radius:22px;padding:22px;box-shadow:0 25px 80px rgba(0,0,0,.35)}.aubamount{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;gap:8px;align-items:center;margin:14px 0 8px}.aubamount label{min-width:0}.aubamount .input{width:100%;text-align:center;font-family:var(--mono);font-weight:700}.aubamount>span{font-size:12px;color:var(--soft)}.aubmodalactions{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}.aubwarn{margin-top:9px;padding:8px 10px;border-radius:10px;background:#FFF4D9;color:#7A5200;font-size:11.5px}
/* Aubrey cards: small held-card reminders in the Doubles panel and a theatrical
   full-card reveal for incoming attacks. Full art stays out of the normal page
   flow and is loaded only when the browser needs it. */
.aubholdrow{display:flex;gap:8px;flex-wrap:wrap;margin-top:5px}.aubmini{display:flex;align-items:center;gap:7px;border:1px solid #E8D9B7;background:#fff;border-radius:10px;padding:5px 7px;color:var(--ink);text-align:left;min-width:0}.aubmini.quiet{opacity:.78}.aubmini img{width:38px;height:57px;object-fit:cover;border-radius:5px;box-shadow:0 3px 8px rgba(43,31,66,.16);flex:none}.aubmini span{display:block;min-width:0}.aubmini b,.aubmini small{display:block}.aubmini b{font-size:11px}.aubmini small{font-size:9.5px;color:var(--soft);margin-top:2px;line-height:1.25}.aubmini:hover{border-color:#CBB77F;transform:translateY(-1px)}
.aubcardmodal{position:fixed;inset:0;z-index:1400;display:flex;align-items:center;justify-content:center;padding:14px;background:rgba(20,14,31,.88);backdrop-filter:blur(7px)}.aubcardmodal[hidden]{display:none}.aubcardstage{position:relative;width:min(94vw,540px);max-height:96vh;display:flex;flex-direction:column;align-items:center;animation:aubCardRise .48s cubic-bezier(.18,.85,.28,1.08) both}.aubcardimage{display:block;max-width:min(84vw,430px);max-height:74vh;width:auto;height:auto;border-radius:14px;box-shadow:0 28px 80px rgba(0,0,0,.52)}.aubcardcopy{width:min(88vw,430px);margin-top:-3px;background:#fff;border-radius:0 0 18px 18px;padding:14px 16px 15px;text-align:center;box-shadow:0 22px 55px rgba(0,0,0,.3)}.aubcardcopy .eyebrow{margin-bottom:4px}.aubcardcopy .h2{font-size:20px;margin-bottom:5px}.aubcardcopy .note{margin:0 0 11px}.aubcardcopy .primary{padding:9px 22px}.aubcardclose{position:absolute;z-index:2;right:5px;top:5px;width:38px;height:38px;border:0;border-radius:50%;background:rgba(25,18,38,.76);color:#fff;font-size:25px;line-height:1}.aubcardclose:hover{background:rgba(25,18,38,.95)}@keyframes aubCardRise{from{opacity:0;transform:translateY(30px) scale(.88)}to{opacity:1;transform:none}}
@media(max-width:559px){.aubcardmodal{padding:8px}.aubcardimage{max-width:88vw;max-height:69vh}.aubcardcopy{width:88vw;padding:11px 13px 13px}.aubcardcopy .h2{font-size:18px}.aubmini img{width:34px;height:51px}}
.tmplayer.missed,.tmplayer.future{background:transparent;border-style:dashed;padding:9px 12px}
.tmplayer.missed .tmnogrid,.tmplayer.future .tmnogrid{margin:0}
.tmplayer.dropped{opacity:.6}
.tmplayer.dropped::after{content:"Dropped from the total";display:block;font-size:10.5px;
  color:var(--soft);margin-top:6px}
/* Four sections in Records were identical weight. Record Book is the one
   people actually browse most, so it gets more depth (shadow only - a border
   as well would repeat the double-cue mistake fixed earlier). Roll of Honour
   gets a warm tint to read as a timeline rather than a data table. Opening
   Words is the quietest: a flatter, near-shadowless archive. */
.card.recdominant{box-shadow:0 22px 48px -26px rgba(43,31,66,.55)}
.card.honcard{background:linear-gradient(150deg,#fff 55%,#FBF6E9)}
.card.wordcard{box-shadow:0 1px 2px rgba(34,31,43,.04)}

/* Roll of Honour: one row per completed week, newest first. Same auto-close
   and reveal-more interaction language as the Record Book and opening-word
   history, so nothing here needs to be learned twice. */
.honwrap{border-top:1px solid var(--line)}
.honwrap:first-child{border-top:0}
.hontop{display:grid;grid-template-columns:84px minmax(0,1fr) minmax(0,1fr) 16px;
  align-items:center;column-gap:8px;width:100%;
  background:transparent;border:0;padding:11px 2px;text-align:left;color:var(--ink);
  border-radius:10px;font-size:13.5px}
.hontop .bchev{grid-column:4}
.honwin,.honspoon{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hontop:hover{background:rgba(154,107,0,.06)}
.honwk{font-family:var(--mono);font-size:11px;color:var(--soft);margin-right:2px}
.honwrap.open .bchev{transform:rotate(90deg)}
.honbody{padding:2px 2px 14px}
.honbody p{margin:0 0 6px;font-size:13px}
.honfinal{margin-top:8px;border-top:1px solid var(--line)}
.honfinal .item{padding:8px 2px}
.honfinal .k{width:22px}

.opts{display:flex;gap:18px;flex-wrap:wrap;margin-top:16px}

.chk{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--soft);cursor:pointer}

.chk input{accent-color:var(--accent);width:15px;height:15px}

/* ---------- opener ---------- */

.friday{background:linear-gradient(150deg,#7A4B00,#B87400);color:#fff;border:0}

.friday .eyebrow{color:rgba(255,255,255,.72)}

.friday h3{font-family:var(--display);font-size:20px;margin:0 0 8px;letter-spacing:-.02em}

.friday p{font-size:13.5px;line-height:1.55;margin:0 0 8px;color:rgba(255,255,255,.9)}

.friday p:last-child{margin-bottom:0}

.friday b{color:#FFE9B8}

.undo{margin-top:13px}

.undochip{background:#fff;border:1.5px solid var(--line);color:var(--ink);border-radius:999px;

  padding:6px 12px;font-size:12px;font-family:var(--mono);margin:6px 6px 0 0}

.undochip:hover{border-color:var(--bad);color:var(--bad)}

#undoList{margin-top:4px}

.adminbar{background:#F6F4FB;border:1px solid var(--line);border-radius:12px;padding:11px 13px;

  font-size:12.5px;color:var(--soft);margin-bottom:12px;display:flex;gap:9px;align-items:center;flex-wrap:wrap}

.adminbar b{color:var(--ink)}

.adminbar .primary{padding:7px 13px;font-size:12px;margin-left:auto}

.opener{background:linear-gradient(150deg,#1F5F4F 0%,#2FA96B 130%);color:#fff}

.opener .eyebrow{color:rgba(255,255,255,.68)}

.opener .note,.opener .byline{color:rgba(255,255,255,.78)}

.word{font-family:var(--display);font-size:38px;font-weight:800;letter-spacing:.1em;

  text-transform:uppercase;margin:4px 0 8px;line-height:1}

.byline{font-size:13px}

.opener .input{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.25);color:#fff}

.opener .input::placeholder{color:rgba(255,255,255,.5)}

.opener .primary{background:#fff;color:#1F5F4F;box-shadow:none}

.opener .primary:hover{background:#F0FBF6}

.opener .primary:disabled{background:rgba(255,255,255,.22);color:rgba(255,255,255,.55)}

.opener .ghost{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.25);color:#fff}

.wordin{font-family:var(--display);font-size:21px;font-weight:800;letter-spacing:.34em;

  text-transform:uppercase;text-align:center;text-indent:.34em;padding:11px 8px}

.wordin:focus{border-color:#fff;box-shadow:0 0 0 4px rgba(255,255,255,.18)}

.wstat{font-family:var(--sans);font-size:12.5px;line-height:1.5;color:rgba(255,255,255,.76);margin:12px 0 0}

.wstat .no{color:#FFD9C2;font-weight:500}

.wstat .yes{color:#D6FBE8;font-weight:500}

.shots{display:flex;gap:7px;flex-wrap:wrap}

.shots button{background:#F6F4FB;border:1.5px solid var(--line);color:var(--ink);

  width:44px;height:44px;font-family:var(--mono);font-size:15px;padding:0;border-radius:13px}

.shots button.on{background:var(--accent);color:#fff;border-color:var(--accent);font-weight:500}

/* ---------- ribbons / rows ---------- */

.prow{display:flex;align-items:center;gap:11px;width:100%;background:transparent;border:0;

  border-radius:13px;padding:11px 9px;text-align:left;color:var(--ink)}

.prow:hover{background:#F7F5FC}

.rank{font-family:var(--mono);font-size:11.5px;color:var(--soft);width:15px;flex:none}

.pname{flex:1;min-width:0;font-size:15px;font-weight:700;letter-spacing:-.01em;

  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.avg{font-family:var(--mono);font-size:15px;width:52px;text-align:right;flex:none}

.played{font-family:var(--mono);font-size:11px;color:var(--soft);width:28px;text-align:right;flex:none}

.ribbon{display:flex;flex:none;width:104px;height:26px;border-radius:999px;overflow:hidden;

  box-shadow:inset 0 0 0 1px rgba(34,31,43,.06)}

.ribbon i{flex:1;display:block;position:relative;animation:slide .4s cubic-bezier(.2,.8,.3,1) both}

.ribbon i.sog::after{content:"";position:absolute;left:50%;top:50%;width:4px;height:4px;margin:-2px 0 0 -2px;

  border-radius:50%;background:rgba(255,255,255,.95)}

.dist{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}

.drow{display:flex;align-items:center;gap:9px}

.dlab{font-family:var(--mono);font-size:12px;color:var(--soft);width:12px;flex:none}

.dbar{height:19px;border-radius:6px;min-width:4px;animation:slide .45s cubic-bezier(.2,.8,.3,1) both}

.dnum{font-family:var(--mono);font-size:11px;color:var(--soft)}

.stats{display:flex;gap:20px;flex-wrap:wrap;border-top:1px solid var(--line);padding-top:15px}

.stat.tappable{cursor:pointer;text-align:left;font:inherit;border:1px solid var(--line)}
.stat.tappable:hover{background:#F7F5FC}
.streakwhy{display:block;margin-top:7px;font-size:11.5px;line-height:1.5;color:var(--soft);font-weight:600}
.stat{display:flex;flex-direction:column;gap:3px}

.stat b{font-family:var(--display);font-size:22px;font-weight:800;letter-spacing:-.03em}

.stat span{font-family:var(--sans);font-size:10px;letter-spacing:.09em;text-transform:uppercase;color:var(--soft)}

.legend{margin:16px 0 0;padding-top:13px;border-top:1px solid var(--line);

  font-family:var(--sans);font-size:11px;color:var(--soft);line-height:1.65}

.existing{display:flex;flex-wrap:wrap;gap:7px;margin-top:13px}

.existing button{background:#F6F4FB;border:1.5px solid var(--line);color:var(--ink);

  padding:7px 14px 7px 8px;font-size:13.5px;font-weight:500;display:flex;align-items:center;gap:8px;border-radius:999px}

.banner{background:#FFF7E3;border:1.5px solid #F2DEA8;color:#7A5A08;border-radius:14px;

  padding:13px 15px;font-size:13px;line-height:1.55;margin-bottom:12px}

.banner b{display:block;margin-bottom:3px}

/* A distinct colour from the ordinary status banner - amber there is
   "something didn't sync, will retry"; this is "you should act on this",
   a different kind of urgency. Stays up until the person actually refreshes;
   nothing else in the app is allowed to clear it, unlike #banner which
   several unrelated successful-save paths hide automatically. */
.banner.stale{background:#FDEDEA;border-color:#F3C6BB;color:#8A3220}
.banner.stale button{margin-top:8px;background:#8A3220;color:#fff;border:0;
  padding:8px 16px;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer}

.item{display:flex;align-items:center;gap:12px;padding:11px 2px;border-top:1px solid var(--line)}

.item:first-child{border-top:0}

.item .k{font-family:var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;

  color:var(--soft);flex:none;width:78px}

.item .v{flex:1;text-align:left;font-size:14.5px;font-weight:700;min-width:0}

.item .v small{display:block;font-family:var(--sans);font-size:11.5px;font-weight:400;color:var(--soft);margin-top:2px}

.empty{font-size:13.5px;color:var(--soft);line-height:1.55;padding:4px 0}

.gatelog{margin-top:14px;font-family:var(--mono);font-size:11px;line-height:1.7;

  background:#F6F4FB;border:1px solid var(--line);border-radius:10px;padding:10px 12px;

  max-height:190px;overflow:auto;color:var(--soft)}

.gatelog .ok{color:var(--good)}

.gatelog .bad{color:var(--bad)}

.gatelog .skip{color:var(--soft);opacity:.75}

.gatelog .step{color:var(--ink)}

.build{font-family:var(--mono);font-size:10px;color:var(--soft);align-self:center;letter-spacing:.04em}

.ft{display:flex;justify-content:space-between;gap:8px;padding:8px 2px;flex-wrap:wrap}

.pair{display:flex;align-items:center;gap:10px;padding:11px 2px;border-top:1px solid var(--line);

  background:transparent;border-left:0;border-right:0;border-bottom:0;width:100%;text-align:left}

.pair:first-child{border-top:0}

.pair{align-items:flex-start}

.pair .who{flex:1;min-width:0;font-size:14px;font-weight:700;display:flex;align-items:center;

  gap:6px;flex-wrap:wrap;line-height:1.35}

.pair .who span:not(.av){overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}

.pair .sc{flex:none}

.pair .sc{font-family:var(--mono);font-size:16px}

.manageplayer{display:flex;align-items:center;gap:10px;padding:10px 2px;border-top:1px solid var(--line);flex-wrap:wrap}.manageplayer:first-child{border-top:0}.manageplayer .pname{font-size:14px}.manageplayer.paused{opacity:.58}.manageplayer .state{font-size:10px;color:var(--soft);font-family:var(--mono);text-transform:uppercase;letter-spacing:.08em}.manageplayer .pname{flex:1 1 auto;min-width:80px;white-space:normal;overflow:visible;text-overflow:clip}
.manageplayer .state{flex:0 0 auto}
.mpacts{display:flex;flex-wrap:wrap;gap:7px;width:100%;padding-left:40px}
.mpacts .ghost{padding:7px 11px;font-size:12px}
/* Once there is room, the actions sit back on the name's line, right-aligned. */
@media (min-width:560px){.mpacts{width:auto;margin-left:auto;padding-left:0}}
.revi{display:flex;align-items:center;gap:8px;margin:0 0 6px;font-size:13px}
.revi span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.revi b{font-family:var(--mono);font-size:13px}
.aubbar{background:#FBF3E4;border-color:#EBD9AE;color:#6B5220}
.auboff{color:var(--soft)}
.aubbar .ghost{margin-top:8px}
.dubmode{font-family:var(--mono);font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--soft);margin:0 0 9px}
.dstrip{display:flex;gap:4px;margin:11px 0 0;flex-wrap:wrap}
.dday{flex:1 1 0;min-width:34px;display:flex;flex-direction:column;align-items:center;gap:1px;
  padding:5px 2px;border-radius:9px;background:#F4F2FA;font-family:var(--mono)}
.dday b{font-size:9px;letter-spacing:.06em;color:var(--soft);font-weight:500}
.dday u{font-size:14px;font-weight:600;text-decoration:none;color:var(--ink)}
.dday i{font-size:9px;font-style:normal;color:var(--accent);font-weight:600}
/* Pair detail: one row per day, showing which partner's score counted. */
.dpdetail{margin:2px 0 4px}
.dprow{display:grid;grid-template-columns:26px 1fr 28px;grid-template-areas:"d s b" ". v v";
  gap:3px 8px;align-items:center;padding:8px 2px;border-top:1px solid var(--line)}
.dprow:first-child{border-top:0}
.dprow.dplive{background:#EEF4FF;border-radius:8px}
.dpday{grid-area:d;font-family:var(--mono);font-size:10px;letter-spacing:.05em;color:var(--soft)}
.dpscores{grid-area:s;display:flex;gap:10px;align-items:center}
.dpm{display:flex;align-items:center;gap:4px;opacity:.45}
.dpm b{font-family:var(--mono);font-size:14px;font-weight:500}
.dpm.dpwin{opacity:1}
.dpm.dpwin b{font-weight:700}
.dpm.dpcover b::after{content:' \2726';font-size:9px;color:var(--accent)}
.dpbest{grid-area:b;font-family:var(--mono);font-size:15px;font-weight:700;text-align:right}
.dprow i{grid-area:v;font-style:normal;font-size:11.5px}
.dpskin{color:#1E7A4B;font-weight:600}
.dpcarry{color:#8A6D2F}
.dplost{color:var(--soft)}
.dday.dlive{background:#EEF4FF;box-shadow:inset 0 0 0 1px #CBDCF8}
.dday.dtot u em{font-style:normal;color:var(--accent)}
.dday.dcarry{background:#FBF3E4}
.dday.dcarry u{color:var(--soft)}
.manageplayer .nameedit{flex:1;min-width:130px}.manageplayer .nameerror{width:100%;padding-left:40px;color:var(--bad);font-size:12px}

#playerManager{position:fixed;z-index:1000;left:50%;top:50%;transform:translate(-50%,-50%);width:min(92vw,560px);max-height:82vh;overflow:auto;margin:0;box-shadow:0 24px 80px rgba(24,16,38,.38);border:1px solid var(--line)}

#playerManager[hidden]{display:none}

/* Phone first: one column, thumb-sized targets, the week table scrolls

   sideways under a pinned name column. From 900px the space is actually used. */

@media (min-width:560px){

  .wrow{grid-template-columns:minmax(110px,1fr) auto auto;gap:12px}

  .wname{grid-column:auto}

  .wcells{gap:5px}

}

@media (min-width:900px){

  .wrap{max-width:1120px}

  .hero{padding:30px 30px 0;border-radius:0 0 32px 32px}

  .brand{font-size:40px}

  .todaygrid{grid-template-columns:repeat(4,minmax(0,1fr))}

  #tab-week{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(0,1fr);

    gap:12px;align-items:start}

  .colmain,.colside{min-width:0}

  .colside{position:sticky;top:12px}

  #tab-recs{column-count:2;column-gap:12px}

  #tab-recs>.card{break-inside:avoid}

  .badgegrid,.todaygrid{gap:10px}

  .stats{gap:26px}

  .cell{width:34px;height:34px;line-height:34px;font-size:14px}

  .ribbon{width:150px}

}

@media (min-width:1200px){

  #tab-form{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);gap:12px;align-items:start}

}

@media (prefers-reduced-motion:reduce){#app *{animation:none!important;transition:none!important}}

@media (max-width:420px){

  .brand{font-size:25px}

  .cell{width:27px;height:27px;line-height:27px;font-size:12px}

  .ribbon{width:82px;height:24px}

  .sigedit{grid-template-columns:repeat(3,40px);grid-template-rows:repeat(3,40px)}

  .todaygrid,.rivalduo,.badgegrid{grid-template-columns:1fr}.todaytop{align-items:center}

}

/* Clearer, colour-coded main navigation. */

.tabs{gap:8px}

.tabs button{

  border:1px solid transparent;

  font-weight:700;

  box-shadow:0 5px 14px -10px rgba(34,31,43,.55);

}

.tabs button:nth-child(1){background:#EDE7F8;color:#4D2A78;border-color:#D9C9F0}

.tabs button:nth-child(2){background:#E4F6F4;color:#087A73;border-color:#BEE8E3}

.tabs button:nth-child(3){background:#E8F0FF;color:#285EAD;border-color:#C9DAF8}

.tabs button:nth-child(4){background:#FFF0DA;color:#9A5711;border-color:#F2D5A8}
.tabs button:nth-child(5){background:#FCE8F2;color:#92345F;border-color:#EFC5D9}
.tabs button.on{background:linear-gradient(135deg,var(--plum),var(--accent));color:#fff;border-color:transparent;box-shadow:0 8px 18px -10px var(--plum)}

/* Footer actions use restrained colours but remain visually distinct. */

.ft .ghost{font-weight:700;box-shadow:0 5px 14px -11px rgba(34,31,43,.5)}

#refresh{background:#E4F6F4;color:#087A73;border-color:#BEE8E3}

#joinPlayer{background:#EDE7F8;color:#5B3383;border-color:#D9C9F0}

#managePlayers{background:#E8F0FF;color:#285EAD;border-color:#C9DAF8}

#exportBtn{background:#FFF0DA;color:#9A5711;border-color:#F2D5A8}

#wipe{background:#FDECEE;color:#B43745;border-color:#F3C6CC}

#refresh:hover,#joinPlayer:hover,#managePlayers:hover,#exportBtn:hover,#wipe:hover{filter:brightness(.96);transform:translateY(-1px)}

/* Keep all five main tabs fully visible on narrow phones. */

@media (max-width:559px){

  .tabs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));overflow:visible}

  .tabs button{width:100%;padding:10px 8px;white-space:normal}

  .tabs button:last-child:nth-child(odd){grid-column:1/-1}

}

/* Opening-word history is compact until the reader asks to see everything. */

.wordtoggle{display:block;width:100%;margin-top:12px}

/* Form rows go two-line on phones. The single-line flex layout survives
   unchanged at 560px and above, where there is room for it. */
@media (max-width:559px){
  .prow{
    display:grid;align-items:center;
    grid-template-columns:15px auto 1fr auto auto;
    grid-template-areas:
      "rank av  name name name"
      ".    rib rib  val  played";
    column-gap:9px;row-gap:7px;padding:12px 9px}
  /* The rank number used to span both rows and centre on their COMBINED
     height. With a taller name row above a shorter ribbon row, that centred
     the digit visually below the middle of the name, closer to the ribbon
     than the name it's meant to number. It now sits only in the name row,
     level with the avatar - a plain numbered-list position. */
  .prow .rank{grid-area:rank;align-self:center}
  .prow .pav{grid-area:av;display:flex;align-items:center}
  .prow .pname{grid-area:name;white-space:normal;overflow:visible;
    font-size:15.5px;line-height:1.2}
  .prow .ribbon{grid-area:rib;width:100%;max-width:158px;height:26px;justify-self:start}
  .prow .avg{grid-area:val;width:auto;min-width:46px}
  .prow .played{grid-area:played;width:auto;min-width:34px}
  /* A bare number beside a score reads as another score. */
  .prow .played::after{content:' played';font-size:9.5px;letter-spacing:.02em}
}

/* Record tiles: the headline stays as compact as the old static row, and the
   evidence behind it only takes up space once somebody asks for it. */
.rtype{border-top:1px solid var(--line)}
.rtype:first-child{border-top:0}
.rtop{display:flex;align-items:center;gap:12px;width:100%;background:transparent;border:0;
  padding:11px 2px;text-align:left;color:var(--ink);border-radius:10px}
.rtop:hover{background:#F7F5FC}
.rtop .k{font-family:var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--soft);flex:none;width:78px}
.rtop .v{flex:1;text-align:left;font-size:14.5px;font-weight:700;min-width:0}
.rtop .v small{display:block;font-weight:600;font-size:11.5px;color:var(--soft);margin-top:2px}
.rtype.open .bchev{transform:rotate(90deg)}
.rlist{padding:2px 2px 14px}
.rrank{margin-top:8px}
.rrow{display:flex;align-items:center;gap:9px;padding:6px 2px;border-top:1px solid var(--line)}
.rrow:first-child{border-top:0}
.rpos{width:16px;flex:none;text-align:right;font-family:var(--mono);font-size:12px;color:var(--soft)}
.rname{flex:1;min-width:0;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rval{font-family:var(--mono);font-size:13px;font-weight:600;color:var(--ink);flex:none}
.evnote{margin:8px 0 0;font-size:12px;line-height:1.55;color:var(--soft)}
.evchips{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px}
.evchip{display:inline-flex;align-items:baseline;gap:6px;background:#F6F4FB;border-radius:9px;
  padding:6px 9px;font-size:11.5px;color:var(--soft)}
.evchip b{font-family:var(--mono);font-size:12px;color:var(--ink);font-weight:700}
.evchip i{font-style:normal;color:var(--soft)}
.evmore{display:inline-flex;align-items:center;padding:6px 9px;font-size:11.5px;
  color:var(--soft);font-family:var(--mono)}
.evhold{display:flex;align-items:center;gap:9px;margin:12px 0 2px;font-size:13.5px}
.evhold:first-child{margin-top:4px}
.evweek{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:4px;margin-top:6px}
.evd{background:#F6F4FB;border-radius:10px;padding:7px 2px;text-align:center}
.evdd{display:block;font-size:9.5px;letter-spacing:.05em;text-transform:uppercase;color:var(--soft)}
.evdv{display:block;font-family:var(--mono);font-size:16px;font-weight:700;margin:1px 0}
.evdn{display:block;font-family:var(--mono);font-size:8.5px;color:var(--soft)}
.evrule{border-top:1px solid var(--line);padding-top:8px}
.evd.evmiss{background:#FBEFEF}.evd.evmiss .evdv{color:#B4453F}
.evd.evfail{background:#F6E3E1}.evd.evfail .evdv{color:#93332E}
.evd.evfuture .evdv{color:var(--soft)}
.evd.evdrop{opacity:.5;text-decoration:line-through}
@media (max-width:559px){
  /* One day per row, full width. Seven columns gave each day about 40px with
     the puzzle number at 8.5px, and four columns was still tight on the
     narrowest handsets; a row reads at any width.

     These rules MUST stay below the base .evweek and .evd declarations. An
     earlier copy sat above them, so the cascade overrode its column count
     while leaving its internal row layout in place - which is what produced
     narrow day-cards with a three-column layout inside them. */
  .evweek{grid-template-columns:1fr;gap:3px}
  .evd{display:grid;grid-template-columns:34px 1fr auto;align-items:center;
    text-align:left;padding:8px 11px;border-radius:9px}
  .evdd{font-size:11px}
  .evdn{font-size:11px;text-align:left}
  .evdv{font-size:15px;margin:0;text-align:right;min-width:18px}
  .evd.evdrop{text-decoration:none;opacity:1}
  .evd.evdrop .evdv{text-decoration:line-through;opacity:.55}
  .evhold{margin:14px 0 4px}
  .evchip{font-size:12px;padding:7px 10px}
  .evchip b{font-size:12.5px}
  .evnote{font-size:12.5px}
  .rlist{padding:2px 0 14px}
  .rtop .k{width:64px;font-size:9.5px}
  .rtop .v{font-size:14px}
}

  



/* ---------- result life-cycle ----------
   The wheel stays exactly as it was: frog-life-cycle.png, whole, inside Add
   Result. Sitting invisibly on top of it are seven cut-out stages, each
   absolutely positioned over its own place on the wheel. Adding a result dims
   the wheel and brings the matching cut-out up out of it.
   The percentages on each .fstage were derived by registering the cut-out
   sheet against the wheel on the numbered badges, which appear in both, so
   they are not hand-eyeballed - but they are tied to this exact pair of files.
   Replacing either one means re-deriving them.
   Cut-outs are frog-stage-7.png down to frog-stage-1.png, named for the badge
   in the picture. data-g carries the Wordle score instead, so data-g="0" (a
   fail) is frog-stage-7.png, the eggs.
   transform-origin points at the outside of the ring on each stage, so growth
   pushes inward across the wheel and nothing runs past the card edge. */
#pasteCard{position:relative;overflow:hidden;isolation:isolate}
.frogcycle-inline{position:relative;width:246px;max-width:100%;aspect-ratio:1374/1145;
  margin:2px auto 13px;pointer-events:none}
.fdisc{position:absolute;inset:0;width:100%;height:100%;display:block;opacity:.90;
  transition:opacity .4s ease,filter .4s ease}
/* The seven .fstage boxes are never drawn. They exist so the code can ask the
   browser where a stage sits on the wheel right now, in real screen pixels,
   and grow the pop-up out of exactly that spot. */
.fstage{position:absolute;margin:0;opacity:0;pointer-events:none}
.fstage img{width:100%;height:100%;display:block}
.frogcycle-inline.picking .fdisc{opacity:.28;filter:saturate(.25)}
#app.no-frogs .frogcycle-inline{display:none}
@media (max-width:559px){.frogcycle-inline{width:222px;margin:1px auto 12px}}

/* The result pop-up. A light veil rather than a solid backdrop, so the week
   table and the rest of the app stay readable behind it. */
.evopop{position:fixed;inset:0;z-index:1300;background:rgba(255,255,255,.42);
  backdrop-filter:blur(1.5px);-webkit-backdrop-filter:blur(1.5px);
  opacity:0;transition:opacity .3s ease;cursor:pointer}
.evopop[hidden]{display:none!important}
.evopop.in{opacity:1}
.evopop img{position:absolute;transform-origin:top left;will-change:transform,filter;
  filter:drop-shadow(0 6px 12px rgba(22,66,53,.14));
  transition:transform .62s cubic-bezier(.2,.8,.3,1),filter .62s ease}
/* The halo is a drop-shadow rather than a ring, so it follows the cut-out's
   own outline - grass, rays and all - instead of boxing it. Gold is the badge
   and champion-ray gold already in the artwork. The transition lands exactly
   on the keyframe the pulse starts from, so the handover at .62s is seamless. */
.evopop.in img{filter:drop-shadow(0 0 9px rgba(241,182,39,.85))
  drop-shadow(0 0 24px rgba(241,182,39,.5)) drop-shadow(0 12px 22px rgba(22,66,53,.18));
  animation:evoHalo 2.2s ease-in-out .62s infinite alternate}
@keyframes evoHalo{
  0%{filter:drop-shadow(0 0 9px rgba(241,182,39,.85))
    drop-shadow(0 0 24px rgba(241,182,39,.5)) drop-shadow(0 12px 22px rgba(22,66,53,.18))}
  100%{filter:drop-shadow(0 0 16px rgba(241,182,39,.95))
    drop-shadow(0 0 40px rgba(241,182,39,.62)) drop-shadow(0 12px 22px rgba(22,66,53,.18))}}
.evohint{position:absolute;left:50%;transform:translateX(-50%);
  bottom:max(26px,calc(env(safe-area-inset-bottom) + 16px));
  font-size:12px;color:#5E786F;background:rgba(255,255,255,.82);
  border-radius:12px;padding:6px 14px}
@media (prefers-reduced-motion:reduce){
  .evopop img{transition:none}
  .evopop.in img{animation:none}}


/* The hub. Only rendered in the Calm layout, where it replaces the tab bar
   entirely - two navigations doing the same job is worse than either alone. */
/* Three visual weights. Today (unchanged, elsewhere) is the hero above this
   grid. Within the grid itself: .hero (This week) is full width with the
   figure at hero scale; .wide (Add result, and Doubles when it needs
   attention) is full width but flatter, a strip rather than a block; plain
   tiles are the two-column grid everything else lives in. */
.hub{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:11px;margin:0 0 14px}

/* Elevation. Two shadows do the "rising off the screen" job: a soft, wide
   one that reads as the tile floating above the page, and a tight, close one
   that reads as where it would touch down - real light doesn't behave any
   other way. An inset top highlight suggests a lit upper edge, the way a
   slightly domed surface catches light. Pressing a tile flattens both: the
   shadows pull in and the highlight fades, so it looks like it has been
   pushed down flush with the page rather than merely scaled - the existing
   scale(.985) tap-feedback is kept underneath, this just gives it somewhere
   to land. */
/* Strengthened from the first pass, which read as too subtle to register as
   raised at all. The far shadow now sits further from the tile and is darker
   (18px offset, .44 opacity vs 14px/.32), so the separation from the page is
   unmistakable rather than merely implied; the near contact shadow is
   likewise darker. Pressed state now drops almost to flush (.06 opacity, no
   spread) rather than just dimming slightly, so the press reads as a real
   change rather than a flicker. */
.hub button{display:block;width:100%;text-align:left;background:var(--card);
  border:1px solid var(--line);border-radius:16px;padding:17px 16px;min-height:120px;
  box-shadow:0 1px 0 rgba(255,255,255,.9) inset,0 2px 4px rgba(34,31,43,.10),
    0 18px 32px -14px rgba(34,31,43,.44);
  font-family:inherit;cursor:pointer;
  transition:transform .12s ease,box-shadow .12s ease}
.hub button:active{transform:scale(.98);
  box-shadow:0 1px 1px rgba(34,31,43,.06)}
@media (hover:hover){
  .hub button:hover{transform:translateY(-3px);
    box-shadow:0 1px 0 rgba(255,255,255,.9) inset,0 3px 5px rgba(34,31,43,.11),
      0 26px 38px -14px rgba(34,31,43,.5)}
}

/* Icon badge: a soft coloured circle the icon sits inside, rather than a bare
   15px glyph on its own - the badge is what reads as an object from across
   the room, the stroke detail is what rewards a closer look. */
.hub .hicon{display:flex;align-items:center;justify-content:center;flex:none;
  width:34px;height:34px;border-radius:50%;margin-bottom:11px;background:var(--bg)}
.hub .hicon svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round}
.hub .hlab{display:block;font-size:12px;color:var(--soft);margin-bottom:6px}
.hub .hbig{display:block;font-family:var(--mono);font-size:26px;font-weight:600;
  color:var(--ink);line-height:1.1}
.hub .hbig.hempty{font-family:inherit;font-size:15px;font-weight:500;color:var(--soft)}
/* The supporting line wraps rather than crops, up to two lines with a
   multi-line ellipsis for the rare case a name and a note are both long.
   Grid items stretch to the tallest in their row by default, so a tile that
   grows pulls its neighbour with it - no extra rule needed. Avatar
   top-aligns rather than centres, since centring against a block that may
   now be two lines tall would sit oddly next to just the first line. */
.hub .hsub{display:flex;align-items:flex-start;gap:6px;font-size:12.5px;color:var(--soft);margin-top:6px;
  min-width:0}
.hub .hsubtext{flex:1;min-width:0;line-height:1.35;white-space:normal;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.hub .hsub .av{flex:none;margin-right:1px;box-shadow:0 0 0 2px var(--card)}
.hub .hsub .av+.av{margin-left:-9px}

/* Three pale families, built from the app's own real Wordle tile colours
   (PAINT.g/.y/.d) rather than an invented palette, cycled across the eight
   ordinary tiles so the grid stops reading as nine identical white cards.
   Each badge gets a slightly stronger version of its card's tint so the icon
   has something to sit on, and the icon stroke itself is a readable mid-tone
   of the same hue - dark enough to see, nowhere near the saturated original. */
/* Deepened from the first pass - white cut down from 55% to 40%, and the
   target colours themselves pulled a step further from white, so the family
   actually reads as a colour rather than nearly vanishing next to a plain
   white card. Borders and badges deepen to match; icon colour darkens
   slightly too, since a paler card needs a bit more contrast underneath it
   to stay legible. */
.hub button.tint-green{background:linear-gradient(155deg,#fff 40%,#DBF1D4);border-color:#B7DFAD}
.hub button.tint-green .hicon{background:#C6E7BC;color:#2F7A3B}
.hub button.tint-gold{background:linear-gradient(155deg,#fff 40%,#F6E9BF);border-color:#E4CB80}
.hub button.tint-gold .hicon{background:#EFDD9C;color:#8A6D1A}
.hub button.tint-grey{background:linear-gradient(155deg,#fff 40%,#E5E5EB);border-color:#CBCBD5}
.hub button.tint-grey .hicon{background:#D8D8E0;color:#454550}

.hub button.live{border:2px solid var(--accent);padding:16px 15px}
.hub button.live .hlab{color:var(--accent)}

.hub button.wide{grid-column:1/-1;display:flex;align-items:center;gap:12px;
  padding:16px 18px;min-height:0;
  /* Literal hex rather than var(--plum2)/var(--accent): var() inside
     linear-gradient() is where a real, confirmed limitation in the test
     tooling silently drops the whole declaration (found earlier this session
     with .accentcard's pre-existing gradient), so anywhere a gradient like
     this needs to be independently verified rather than taken on faith, the
     literal value is used instead. Same colours, #5B3383/#6C4AB6. */
  background:linear-gradient(135deg,#5B3383,#6C4AB6);border-color:transparent;
  box-shadow:0 1px 0 rgba(255,255,255,.18) inset,0 16px 30px -16px rgba(43,31,66,.55)}
.hub button.wide .hicon{margin:0;flex:none;width:30px;height:30px;background:rgba(255,255,255,.16)}
.hub button.wide .hicon svg{width:16px;height:16px}
.hub button.wide .hicon,.hub button.wide .hlab,.hub button.wide .hsub{color:#fff}
.hub button.wide .hlab{margin:0;flex:1;font-size:15px;font-weight:600}
.hub button.wide .hsub{font-size:13px;opacity:.85}
/* Completed: the same purple-family gradient would read as "still needs
   doing", so it switches to the pale green family instead - green already
   means success everywhere else the app uses PAINT.g, this is the same idea
   applied to a full-width action bar rather than a grid tile. */
.hub button.wide.done{background:linear-gradient(135deg,#fff 55%,#EAF6E7);
  box-shadow:0 1px 0 rgba(255,255,255,.8) inset,0 1px 2px rgba(34,31,43,.06),
    0 14px 26px -16px rgba(34,31,43,.32)}
.hub button.wide.done .hicon{background:#DCF0D8;color:#1E7A4B}
.hub button.wide.done .hlab,.hub button.wide.done .hsub{color:var(--ink)}
.hub button.wide.done .hsub{opacity:1;color:var(--soft)}
/* Doubles promoted to a wide strip for the day because there is something
   only the pair can resolve - an attack landed, or a pledge waiting on you.
   Warmer than the plain accent border .live uses for a merely-carrying pot,
   since this is a call to action rather than a state to notice in passing. */
.hub button.wide.urgent{background:#FBF3E4;border-color:#E3B341;
  box-shadow:0 1px 0 rgba(255,255,255,.8) inset,0 1px 2px rgba(34,31,43,.06),
    0 14px 26px -16px rgba(154,107,0,.2)}
.hub button.wide.urgent .hlab{color:#8A6D2F;font-weight:600}
.hub button.wide.urgent .hicon{background:rgba(154,107,0,.12);color:#8A6D2F}

.hubback{display:flex;align-items:center;gap:8px;margin:0 0 16px}
.hubback #hubBackBtn{background:none;border:0;box-shadow:none;padding:2px 4px 2px 0;
  color:var(--accent);font-weight:600;font-size:14px}
.hubback #hubBackBtn::before{content:'\2039\00a0';font-weight:700}
.hubback span{font-family:inherit;font-weight:600;font-size:14px;color:var(--ink)}
.hubback span::before{content:'\00b7';margin:0 7px;color:var(--line);font-weight:400}
/* Today is the hub's hero and has no business following you into a
   destination - it is not what you came here for, and it pushed the thing
   you tapped for below the fold every time. Collapsed to a strip it still
   answers "where are we", just without reclaiming the whole screen. */
/* Today, and the page's own title header above it, belong to the hub screen
   only. Neither has anything to do with the thing you tapped a tile to see,
   and both used to survive every drill-in, pushing the actual destination
   further down the page than it needed to be. Hidden outright rather than
   shrunk - a collapsed strip was tried first and still cost a full line plus
   its own top/bottom margins for content nobody had come to look at. */
#app.skin-calm.hub-drilled .todaycard,
#app.skin-calm.hub-drilled .hero .brand,
#app.skin-calm.hub-drilled .hero .herosub,
#app.skin-calm.hub-drilled .hero .weave{display:none}
/* The player-switcher chip and the banner live in or right after the header
   and have nothing to do with Today, so they stay reachable on every screen -
   only the title and the "state of play" strip disappear. */
#app.skin-calm.hub-drilled .hero{padding-bottom:0}
#app.skin-calm.hub-drilled .herotop{justify-content:flex-end}
.hubbackfoot{display:flex;justify-content:center;margin:22px 0 4px}
.hubbackfoot button{min-width:140px;text-align:center}
/* Stands in for a destination card that has hidden itself because it
   genuinely has nothing to show - no shot called, no live projection - rather
   than the hub presenting a blank space or removing the tile that got you
   here, which would make the grid change shape depending on the day. */
.hubempty{background:var(--card);box-shadow:var(--sh);border-radius:14px;
  padding:22px 16px;margin:0 0 14px;text-align:center;font-size:13.5px;color:var(--soft)}


/* Phone-first Hub opening: keep Today useful, but stop it occupying the first
   screen. Classic is untouched; this applies only to the Hub layout on phones. */
.hub button.summarywide{grid-column:1/-1;display:grid;
  grid-template-columns:auto minmax(0,1fr) auto;align-items:center;column-gap:13px;
  min-height:0;padding:17px 18px}
.hub button.summarywide .hicon{margin:0;width:40px;height:40px}
.hub button.summarywide .hsumtext{display:block;min-width:0}
.hub button.summarywide .hlab{margin-bottom:3px}
.hub button.summarywide .hbig{font-size:28px}
.hub button.summarywide .hsub{margin-top:4px}
.hub button.summarywide .hsumchev{font-size:27px;line-height:1;color:var(--soft)}

@media (max-width:559px){
  #app.skin-calm .hero{padding:11px 15px 0;margin-bottom:8px;border-radius:0 0 16px 16px}
  #app.skin-calm .brand{font-size:21px;line-height:1;letter-spacing:-.025em;white-space:nowrap}
  #app.skin-calm .brand br{display:none}
  #app.skin-calm .herosub{font-size:9.5px;margin-top:4px}
  #app.skin-calm .chip.me{padding:4px 10px 4px 5px;font-size:11.5px}
  #app.skin-calm .todaycard{padding:12px 13px;border-radius:16px;margin-bottom:11px}
  #app.skin-calm .todaytop{align-items:center}
  #app.skin-calm .todaytop .eyebrow{margin-bottom:3px;font-size:9.5px}
  #app.skin-calm .todaytop h2{font-size:17px;white-space:nowrap}
  #app.skin-calm .todaygrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:7px;margin-top:9px}
  #app.skin-calm .todaygrid .pulse{padding:8px 9px;border-radius:11px;gap:7px;min-width:0}
  #app.skin-calm .todaygrid .pulse .ico{font-size:17px}
  #app.skin-calm .todaygrid .pulse b{font-size:12px}
  #app.skin-calm .todaygrid .pulse small{font-size:9.5px}
  #app.skin-calm .todaygrid .pulse-opener,
  #app.skin-calm .todaygrid .pulse-week,
  #app.skin-calm .todaygrid .pulse-streak{display:none}
  #app.skin-calm .commentary,#app.skin-calm .awards{display:none}
  #app.skin-calm .hub{gap:9px}
  #app.skin-calm .hub button{min-height:106px;padding:14px 13px}
  #app.skin-calm .hub button.summarywide{min-height:0;padding:14px 15px}
  #app.skin-calm .hub button.wide{padding:13px 15px}
  #app.skin-calm .hub .hbig{font-size:23px}
  #app.skin-calm .hub button.summarywide .hbig{font-size:27px}
  #app.skin-calm .hub button.actionhalf{min-height:98px}
  #app.skin-calm .hub button.addaction{
    background:linear-gradient(145deg,var(--plum),var(--plum2));color:#fff;border-color:transparent
  }
  #app.skin-calm .hub button.addaction .hlab,
  #app.skin-calm .hub button.addaction .hbig,
  #app.skin-calm .hub button.addaction .hsub,
  #app.skin-calm .hub button.addaction .hicon{color:#fff}
  #app.skin-calm .hub button.addaction .hsub{opacity:.72}
}


/* Compact mobile header: three score ribbons above and three below. The upper
   ribbons may sit behind the iPhone camera island; keeping them there lets the
   whole banner remain as shallow as the previous compact version. */
.weave-top{display:none}
@media (max-width:559px){
  #app.skin-calm .hero{
    padding:7px 15px 0;
  }
  #app.skin-calm .weave-top{
    display:flex;flex-direction:column;gap:1px;margin:0 -15px 4px;padding:0
  }
  #app.skin-calm .weave-bottom{
    display:flex;flex-direction:column;gap:2px;margin:5px -15px 0;padding:0 0 2px
  }
  #app.skin-calm .weave-top .wrib,
  #app.skin-calm .weave-bottom .wrib{height:4px}
  #app.skin-calm .herotop{min-height:42px;align-items:center}
}


/* Today's compact status cards need to wrap gracefully on narrow phones.
   Long pair names should take a second line rather than collide with the
   neighbouring "played" card. */
@media (max-width:559px){
  #app.skin-calm .todaygrid .pulse{
    align-items:flex-start;
    min-width:0;
  }
  #app.skin-calm .todaygrid .pulse>span:last-child{
    min-width:0;
    flex:1 1 auto;
  }
  #app.skin-calm .todaygrid .pulse b{
    white-space:normal;
    overflow:visible;
    text-overflow:clip;
    line-height:1.2;
    overflow-wrap:anywhere;
  }
  #app.skin-calm .todaygrid .pulse small{
    white-space:normal;
    line-height:1.2;
    overflow-wrap:anywhere;
  }
  #app.skin-calm .todaygrid .pulse-doubles .dubav{
    flex:0 0 auto;
    margin-top:1px;
  }
}


/* Hub navigation is deliberately more conspicuous than an ordinary ghost
   button. These are the escape routes from a drilled-in destination, so they
   should be discoverable at a glance on a phone. */
#app .hubback #hubBackBtn,
#app .hubbackfoot #hubBackBtn2{
  background:linear-gradient(145deg,var(--accent) 0%,var(--plum2) 100%);
  color:#fff;
  border:1px solid rgba(255,255,255,.18);
  box-shadow:0 8px 18px -9px rgba(91,51,131,.72);
  font-weight:800;
}
#app .hubback #hubBackBtn{
  padding:8px 13px;
  border-radius:999px;
  font-size:12.5px;
}
#app .hubback #hubBackBtn::before{content:'‹ ';font-weight:800}
#app .hubbackfoot #hubBackBtn2{
  padding:10px 18px;
  border-radius:999px;
  min-width:150px;
}
#app .hubback #hubBackBtn:hover,
#app .hubbackfoot #hubBackBtn2:hover{
  background:linear-gradient(145deg,var(--plum2) 0%,var(--accent) 100%);
  color:#fff;
  border-color:rgba(239,180,35,.55);
}
#app .hubback #hubBackBtn:focus-visible,
#app .hubbackfoot #hubBackBtn2:focus-visible{
  outline:3px solid #EFB423;
  outline-offset:2px;
}

/* ============================== CALM LAYOUT ==============================
   An alternative skin, chosen per device and stored only in localStorage, so
   one person can try it without changing what anyone else sees.
   It is deliberately CSS only. Nothing in the markup moves and no script
   behaviour changes, so it cannot break the scoring and reverting it is one
   class on #app.
   The complaint it answers is paging up and down through a stack of identical
   boxes. Three things cause that: every section carries the same card chrome
   so nothing reads as more or less important; the padding is generous enough
   that a one-line note occupies a whole screen; and the paste box holds six
   empty rows all day. So: strip the chrome from secondary sections and let
   their eyebrow act as the heading, tighten the rhythm, keep exactly one hero,
   and collapse the paste box until it is wanted. */

/* Secondary sections lose the box and become headed passages on the page.
   The eyebrow already reads as a section label - it just needed to stop
   sitting inside a container. */
#app.skin-calm .card{background:none;box-shadow:none;border-radius:0;padding:0 2px;
  margin:0 0 20px;border-bottom:1px solid var(--line);padding-bottom:18px}
#app.skin-calm .card:last-child{border-bottom:0}
/* Stripping every card down to a plain heading went too far for these three:
   flattened to text on a flat background, Projected finish, the Record book
   and the Roll of Honour read as afterthoughts rather than things worth
   opening. They get a real card back, with colour - drawn from the palette
   already in use elsewhere (the accent purple, the plum of Today's hero)
   rather than anything new, so it reads as part of the same app. */
#app.skin-calm #projCard{background:linear-gradient(155deg,#fff 55%,var(--accent-soft));
  border:1px solid #DCCCF2;border-radius:16px;padding:15px 16px 16px;box-shadow:var(--sh)}
#app.skin-calm #projCard .eyebrow{color:var(--accent)}
#app.skin-calm .card.recdominant{background:#fff;border:1px solid var(--line);
  border-radius:18px;padding:16px;box-shadow:0 22px 48px -26px rgba(43,31,66,.28)}
#app.skin-calm .card.honcard{background:linear-gradient(155deg,#fff 60%,#F3ECFF 130%);
  border:1px solid #E4D7F7;border-radius:16px;padding:15px 16px 16px;box-shadow:var(--sh)}
#app.skin-calm .card.honcard .eyebrow{color:var(--plum2)}
#app.skin-calm .eyebrow{margin-bottom:8px;letter-spacing:.12em;color:var(--soft)}

/* One hero. Today keeps its card, gains weight, and goes full width so the eye
   has somewhere to land before the quieter material below. */
#app.skin-calm .todaycard{margin:0 -4px 20px;padding:20px 18px;border-radius:20px;
  background:linear-gradient(150deg,#2B1F42 0%,#4A2E75 100%);border:0;color:#F3F0FB}
#app.skin-calm .todaycard .eyebrow{color:#B9A9E8}
#app.skin-calm .todaycard h2{color:#FFF}
#app.skin-calm .todaycard::after{background:rgba(255,255,255,.07)}
#app.skin-calm .todaycard .pulse{background:rgba(255,255,255,.09);border-color:transparent}
#app.skin-calm .todaycard .pulse b{color:#FFF}
#app.skin-calm .todaycard .pulse small{color:#C6BAE8}

/* Cards that are genuinely objects keep their surface: the week table, the
   doubles card, the panels you open and close. */
#app.skin-calm .myweekcard,#app.skin-calm #playerManager,#app.skin-calm #dataCard,
#app.skin-calm #detailCard,#app.skin-calm .dubcard{background:var(--card);
  box-shadow:var(--sh);border-radius:18px;padding:16px;border-bottom:0}

/* The one-line notes were never worth a box of their own. Projected finish
   used to be included here too, but a flat one-line treatment was exactly
   the complaint - it now gets the full card treatment declared above instead,
   so it is deliberately absent from this list. */
#app.skin-calm .opener,#app.skin-calm .friday,
#app.skin-calm #rogueCard{border-bottom:0;margin-bottom:12px;padding-bottom:0;
  font-size:13px;color:var(--soft)}
#app.skin-calm .opener .eyebrow,#app.skin-calm .friday .eyebrow,
#app.skin-calm #rogueCard .eyebrow{display:none}

/* Friday's word-selection notice keeps its own high-contrast surface in Calm.
   The generic Calm card rule used to remove the brown background while the
   .friday paragraph text stayed white, making the copy almost disappear. */
#app.skin-calm .friday{
  background:linear-gradient(150deg,#6B4306 0%,#A66D08 100%);
  color:#fff;border:0;border-radius:18px;padding:18px 18px 16px;
  margin:0 0 14px;box-shadow:0 14px 30px -20px rgba(74,46,0,.65)
}
#app.skin-calm .friday h3{color:#fff}
#app.skin-calm .friday p{color:rgba(255,255,255,.94)}
#app.skin-calm .friday b{color:#FFE7A6}

/* Saturday's opening word is also a high-contrast notice, not a flat note.
   Keep the green surface so the byline, note and correction control remain
   legible in Calm/Hub mode. */
#app.skin-calm .opener{
  background:linear-gradient(150deg,#1F5F4F 0%,#2FA96B 130%);
  color:#fff;border:0;border-radius:18px;padding:16px 18px;
  margin:0 0 14px;box-shadow:0 14px 30px -20px rgba(20,75,60,.55)
}
#app.skin-calm .opener .word{color:#fff}
#app.skin-calm .opener .byline,
#app.skin-calm .opener .note{color:rgba(255,255,255,.90)}
#app.skin-calm .opener .ghost{
  background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.38);color:#fff
}
#app.skin-calm .opener .ghost:hover{
  background:rgba(255,255,255,.22);border-color:rgba(255,255,255,.55);color:#fff
}

/* The paste box is the tallest thing on the page and empty most of the day.
   Collapsed it is one line; focusing it opens it. */
#app.skin-calm #pasteCard{background:var(--card);box-shadow:var(--sh);
  border-radius:14px;padding:9px 11px;border-bottom:0;margin-bottom:14px}
#app.skin-calm #pasteCard .eyebrow{margin-bottom:5px}
#app.skin-calm #pasteCard .ta{min-height:0;height:34px;overflow:hidden;
  transition:height .25s ease;margin-bottom:6px;padding:8px 10px}
#app.skin-calm #pasteCard .ta:focus,#app.skin-calm #pasteCard.open .ta{height:112px;overflow:auto}
#app.skin-calm #pasteCard .frogcycle-inline{width:178px;margin:0 auto 7px}
#app.skin-calm #pasteCard .undo{margin-top:4px}

/* Tighter vertical rhythm throughout - this is most of the paging saved. */
#app.skin-calm .tabs{margin-bottom:16px}
#app.skin-calm .note{font-size:12.5px;line-height:1.55;color:var(--soft);margin:6px 0 0}
#app.skin-calm .evnote{margin:7px 0 0}
#app.skin-calm .wrow{padding:9px 0}


.hdright{display:flex;flex-direction:column;gap:6px;align-items:flex-end}
.chip.grp{background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.22);color:#fff;
  padding:5px 12px;border-radius:999px;font-size:12px;font-weight:600;cursor:pointer;
  display:flex;align-items:center;gap:6px}
.chip.grp:hover{background:rgba(255,255,255,.2)}
.grpmenu{position:fixed;inset:0;background:rgba(20,16,32,.55);display:flex;align-items:center;
  justify-content:center;padding:20px;z-index:50}
.grpsheet{background:#fff;border-radius:18px;padding:16px;max-width:380px;width:100%}
.grpsheet h3{margin:0 0 4px;font-size:18px;font-weight:800;letter-spacing:-.02em}
.grpsheet p{margin:0 0 14px;font-size:12.5px;color:var(--soft);line-height:1.5}
.grpopt{display:flex;align-items:center;gap:11px;width:100%;background:#F7F5FC;border:1.5px solid transparent;
  border-radius:13px;padding:12px;margin-bottom:8px;cursor:pointer;text-align:left;font-family:var(--sans)}
.grpopt.on{border-color:var(--accent);background:var(--accent-soft)}
.grpacts{display:flex;gap:8px;margin-top:12px}
.grpacts button{flex:1;padding:11px 10px;font-size:13px}
.grpform{margin-top:12px;display:flex;flex-direction:column;gap:10px}
.grpform label{font-size:12px;color:var(--soft);display:flex;flex-direction:column;gap:5px}
.grpform label.chk{flex-direction:row;align-items:center;gap:8px;font-size:12.5px}
.grpform .input{font-size:15px}
.devcode{font-family:var(--mono);font-size:20px;letter-spacing:.28em;color:var(--accent)}
.code-in{font-family:var(--mono);letter-spacing:.3em;text-transform:uppercase;text-align:center}
.code{display:block;font-family:var(--mono);font-size:26px;letter-spacing:.28em;text-align:center;
  background:var(--accent-soft);color:var(--accent);border-radius:12px;padding:12px;margin:10px 0}
.grpmsg{font-size:12.5px;color:var(--soft);line-height:1.5;margin:0}
.grpopt b{display:block;font-size:15px}
.grpopt small{display:block;font-size:11.5px;color:var(--soft);margin-top:2px}

.grpsheet{max-height:90dvh;overflow:auto}.grpform .input{font-size:16px}

html,body{margin:0;min-height:100%;background:var(--bg);font-family:var(--sans);color:var(--ink)}
#app{min-height:100dvh}

.award b{color:var(--ink)}
