/* ============================================================================
   THE MOUND METHOD — the app.

   This is Jack's training book, not a dashboard. The whole visual system is
   three things: white paper, hard navy rules, and one very loud face.

   RULES OF THE SYSTEM
   1. No cards. Nothing floats. Sections are divided by rules, edge to edge.
      A card is a box a designer reaches for when the hierarchy is not doing
      its job. The hierarchy does its job here.
   2. No rounded corners, no gradients, no drop shadows. Anywhere. Ever.
   3. Two faces only: Archivo Black for display, Archivo for everything else.
   4. Numbers are furniture. Exercise indices, dates and day numbers are set
      large in the gutter the way a printed program sets them.
   5. Navy is used in slabs, not accents. When something matters, the whole
      band goes navy and the type goes white.
   ============================================================================ */

:root{
  --navy:#0B1F3A; --navy-deep:#04172F; --navy-mid:#22385A; --blue:#3D5B87;
  --paper:#FFFFFF; --gray:#F2F4F7; --gray-2:#E7EBF1;
  --rule:#0B1F3A;                  /* structural rules are navy, not grey */
  --line:#D9DFE8; --line-soft:#EBEEF3;
  /* The ghosted index numerals (01, 02, 03) are furniture, but they are still
     text on screen. #D9DFE8 measured 1.34:1 and failed even the large-text
     threshold. #838D9B is 3.36:1 on white and 3.05:1 on --gray: still clearly
     secondary, still legible, still passes. */
  --index:#838D9B;
  /* #75839A measured 3.84:1 on white and failed WCAG AA for body text.
     #606D83 is the same hue at 5.23:1 on white. Do not lighten it. */
  --ink:#2E3A50; --quiet:#606D83;
  --ok:#1F6B4A; --warn:#8A5A1B;
  --display:"Archivo Black","Arial Black","Helvetica Neue",Impact,sans-serif;
  --sans:"Archivo",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  /* ---- motion ----
     One curve, three durations, and nothing else. Motion here communicates
     state, hierarchy or a physical press. Nothing animates because it can.
       t1  press, hover, colour        - felt, not seen
       t2  something arriving          - the daily lines, a dialog, a row
       t3  a whole view changing       - the only duration you consciously notice
     If an animation is noticed before the content is, it is too long. */
  --ease:cubic-bezier(.2,.7,.3,1);
  /* Three durations, and nothing else anywhere. 140 for a state change you
     should not notice, 280 for something arriving or leaving, 360 for a whole
     screen. Three animations used to carry hand-typed 260ms, 220ms and 620ms,
     which is not a slower feel, it is three people guessing. */
  --t1:140ms; --t2:280ms; --t3:360ms;
  --fast:140ms;
  --pad:clamp(18px,5vw,40px);      /* one horizontal rhythm for the whole app */
}
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--sans);
     font-size:16.5px;line-height:1.55;-webkit-font-smoothing:antialiased;
     font-variant-numeric:tabular-nums}
img{max-width:100%;display:block}
a{color:inherit}
:focus-visible{outline:2px solid var(--navy);outline-offset:2px}
[hidden]{display:none!important}
/* Visible to a screen reader, not to the eye. Used where a single letter is
   enough on screen but a day name is what should be read out. */
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* --------------------------------------------------------------- type ---- */
/* DISPLAY  Archivo Black. The workout. The athlete. The month. Rare and huge.
   SECTION  Archivo 700, uppercase, letterspaced. Structural, never decorative.
   BODY     Archivo 400-500. The easiest thing on the screen to read. */
h1{font-family:var(--display);color:var(--navy);margin:0;font-weight:400;
   text-transform:uppercase;letter-spacing:-.045em;line-height:.8;
   font-size:clamp(44px,10vw,80px)}
h2{font-family:var(--sans);color:var(--navy);margin:0;font-weight:700;
   text-transform:uppercase;letter-spacing:.16em;line-height:1.1;font-size:11px}
h3{font-family:var(--sans);color:var(--navy);margin:0;font-weight:700;
   font-size:17px;line-height:1.25}
p{margin:0 0 .85em}p:last-child{margin-bottom:0}

/* The one label style in the product. A rule, a gap, then the word. */
.label,.count{display:block;font-family:var(--sans);font-weight:700;font-size:10px;
  letter-spacing:.24em;text-transform:uppercase;color:var(--quiet);line-height:1}
.muted{color:var(--quiet);font-size:15px}
.hint{font-size:13.5px;line-height:1.5;color:var(--quiet);margin:8px 0 0;max-width:60ch}

.wrap{max-width:820px;margin:0 auto;padding:0 var(--pad)}
.wrap-wide{max-width:1240px}
/* Full-bleed inside .wrap: this is how a section breaks the margin and runs
   edge to edge without a wrapper element. */
.bleed{margin-left:calc(var(--pad) * -1);margin-right:calc(var(--pad) * -1);
  padding-left:var(--pad);padding-right:var(--pad)}

/* ------------------------------------------------------------ sections ---- */
/* Every block of the app is a SECTION: a navy rule, a label, then content.
   This replaces every card in the product. */
.sec{border-top:2px solid var(--rule);padding:26px 0 34px;margin:0}
.sec-sub{border-top:1px solid var(--line)}
.sec > .label{margin-bottom:18px}
.sec-head{display:flex;align-items:baseline;justify-content:space-between;
  gap:14px;flex-wrap:wrap;margin-bottom:18px}
.sec-head .label{margin:0}

/* THE TOP OF A SCREEN.
   The title, then what there is of it, then the tools. Three stacked blocks
   rather than one flex row, because a flex row with a search field in it fits a
   laptop and amputates the button on a phone.

   The top padding exists because the app bar is sticky: without it the cap of a
   116px display heading sits underneath the bar and reads as clipped. */
.screen-head{padding-top:26px;margin-bottom:16px}
.screen-head h1{line-height:.9}
.screen-head .count{margin-top:10px}
.screen-tools{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:22px}
.screen-tools input[type=search]{flex:1 1 180px;min-width:0}
.screen-tools .btn{flex:0 0 auto}
@media(max-width:400px){
  .screen-tools .btn{flex:1 1 100%}
}

/* ------------------------------------------------------------- buttons ---- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;
  font-family:var(--sans);font-size:13px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;padding:14px 22px;min-height:52px;
  border:2px solid transparent;border-radius:0;background:none;cursor:pointer;
  text-decoration:none;white-space:nowrap;
  transition:background var(--fast) var(--ease),color var(--fast) var(--ease),
             border-color var(--fast) var(--ease),transform 90ms var(--ease)}
.btn:active{transform:translateY(1px)}
.btn-navy{background:var(--navy);color:#fff;border-color:var(--navy)}
.btn-navy:hover{background:var(--navy-mid);border-color:var(--navy-mid)}
.btn-out{border-color:var(--navy);background:transparent;color:var(--navy)}
.btn-out:hover{background:var(--navy);color:#fff}
/* Text-styled, still a 52px target, negative margin so alignment holds. */
.btn-ghost{color:var(--navy);padding:14px 6px;margin:0 -6px;border:0;
  text-decoration:underline;text-underline-offset:4px;text-decoration-thickness:1px}
.btn-ghost:hover{color:var(--blue)}
.btn-sm{padding:10px 15px;font-size:11px;min-height:44px}
.btn-block{width:100%}
.btn[disabled]{opacity:.4;cursor:default}
/* On navy */
.on-navy .btn-out{border-color:rgba(255,255,255,.55);color:#fff}
.on-navy .btn-out:hover{background:#fff;color:var(--navy);border-color:#fff}

/* ------------------------------------------------- the coach's top bar ---- */
/* The coach dashboard is a desktop tool most of the time, so it keeps a
   conventional bar. Same vocabulary: a hard rule, uppercase micro-type. */
.top{background:var(--paper);border-bottom:2px solid var(--rule);position:sticky;top:0;z-index:50}
.top-in{display:flex;align-items:center;gap:14px;height:70px}
@media(max-width:620px){
  /* The name is not worth two lines on a phone. Sign out says whose account it is. */
  .top-who{display:none}
  .top-in{height:62px}
  .top-brand b{letter-spacing:.16em}
}
.top-brand{display:flex;align-items:center;gap:11px;text-decoration:none;flex:none;min-height:44px}
.top-brand img{width:30px;height:30px}
.top-brand b{font-family:var(--sans);font-size:10.5px;font-weight:700;color:var(--navy);
  line-height:1.4;letter-spacing:.2em;text-transform:uppercase}
.top-spacer{flex:1}
.top-who{font-size:10.5px;color:var(--quiet);text-align:right;line-height:1.4;
  font-weight:700;letter-spacing:.18em;text-transform:uppercase}
.top-who b{display:block;color:var(--navy);font-weight:700}

/* ---------------------------------------------------------- the app bar ---- */
.appbar{background:var(--navy);color:#fff;position:sticky;top:0;z-index:50;
  padding-top:env(safe-area-inset-top)}
.appbar-in{display:flex;align-items:center;gap:11px;height:56px;
  padding:0 var(--pad);max-width:820px;margin:0 auto}
.appbar img{width:24px;height:24px}
.appbar b{font-family:var(--sans);font-weight:700;font-size:11px;
  text-transform:uppercase;letter-spacing:.22em}
.appbar .sp{flex:1}
.appbar select{width:auto;max-width:46vw;background:transparent;color:#fff;
  border:0;border-bottom:1px solid rgba(255,255,255,.4);min-height:44px;
  font-size:12px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:4px 2px}
.appbar select option{color:var(--navy)}
/* Sign out belongs to the bar, not to the page. A bordered web button sitting
   on a navy bar is the single most website-looking thing in either app. */
.appbar-out{background:none;border:0;color:rgba(255,255,255,.72);cursor:pointer;
  font-family:var(--sans);font-weight:700;font-size:10px;letter-spacing:.2em;
  text-transform:uppercase;min-height:44px;padding:0 0 0 14px;
  transition:color var(--t1) var(--ease)}
.appbar-out:hover{color:#fff}
.appbar .top-who{color:rgba(255,255,255,.72);font-size:10px;letter-spacing:.18em;
  text-align:right;line-height:1.3;max-width:44vw;overflow:hidden}
.appbar .top-who b{color:#fff;display:block;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}

/* ------------------------------------------------------- the bottom nav ---- */
/* No icons. Icons in a four-item bar are the single clearest tell of a generic
   dashboard, and they say nothing that the word does not. */
.bottomnav{position:fixed;left:0;right:0;bottom:0;z-index:60;background:var(--navy);
  display:grid;grid-template-columns:repeat(4,1fr);
  padding-bottom:env(safe-area-inset-bottom)}
/* The coach app has two destinations, not four. Same bar, same rules, honest
   about how many things there are rather than padding it out to fill the row. */
.bottomnav.nav-2{grid-template-columns:repeat(2,1fr)}
.bottomnav.nav-3{grid-template-columns:repeat(3,1fr)}
.bottomnav button{background:none;border:0;border-top:3px solid transparent;
  padding:17px 4px 16px;font-family:var(--sans);font-size:10.5px;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:rgba(255,255,255,.62);
  cursor:pointer;min-height:56px;transition:color var(--fast) var(--ease)}
.bottomnav button:hover{color:#fff}
.bottomnav button[aria-current="page"]{color:var(--navy);background:#fff;border-top-color:#fff}
@media(min-width:900px){
  .bottomnav.nav-2{grid-template-columns:repeat(2,auto)}
  .bottomnav.nav-3{grid-template-columns:repeat(3,auto)}
  .bottomnav{position:static;grid-template-columns:repeat(4,auto);justify-content:start;
    background:transparent;border-bottom:2px solid var(--rule);max-width:820px;
    margin:0 auto;padding:0 var(--pad)}
  .bottomnav button{color:var(--quiet);border-top:0;border-bottom:3px solid transparent;
    padding:20px 22px 17px;margin-bottom:-2px}
  .bottomnav button:hover{color:var(--navy)}
  .bottomnav button[aria-current="page"]{color:var(--navy);background:transparent;
    border-bottom-color:var(--navy);border-top-color:transparent}
}
.page{padding-top:0;padding-bottom:104px}
@media(min-width:900px){.page{padding-bottom:56px}}

/* ------------------------------------------------------------ the briefing ---
   Motivation and tip are not two sections that happen to sit next to each
   other. They are one note, under one masthead, and the heavy rule at the
   bottom is where the note ends and the work starts. */
.brief{border-bottom:3px solid var(--rule);padding-bottom:8px;margin-bottom:0}
.brief-head{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;
  flex-wrap:wrap;padding:36px 0 22px}
.brief-word{font-family:var(--display);font-weight:400;text-transform:uppercase;
  color:var(--navy);letter-spacing:-.05em;line-height:.78;
  font-size:clamp(52px,15vw,116px);margin:0}
.brief-date{font-family:var(--sans);font-weight:700;font-size:10px;letter-spacing:.26em;white-space:nowrap;
  text-transform:uppercase;color:var(--quiet);padding-bottom:clamp(6px,1.4vw,14px);
  text-align:right;line-height:1.6}
@media(max-width:400px){
  .brief-head{align-items:flex-start;flex-direction:column;gap:14px;padding-top:30px}
  .brief-date{text-align:left;max-width:none;padding-bottom:0}
}

/* ------------------------------------------------------------- the hero ---- */
/* A navy slab, the full width of the screen, with the workout name as large as
   the word will go. This is the one thing the athlete opened the app for. */
.today-hero{background:var(--navy);color:#fff;
  margin-left:calc(50% - 50vw);margin-right:calc(50% - 50vw);
  padding:clamp(30px,7vw,62px) max(var(--pad),calc(50vw - 410px + var(--pad))) clamp(28px,5vw,50px)}
.today-hero .kicker{display:flex;align-items:center;gap:12px;
  color:rgba(255,255,255,.62);font-size:10.5px;font-weight:700;
  letter-spacing:.22em;text-transform:uppercase}
.today-hero .kicker::after{content:"";flex:1;height:1px;background:rgba(255,255,255,.28)}
/* A slab title is an h2, because the masthead above it is the h1. It is styled
   like a headline because it is one; the level is about structure, not size. */
.today-hero h1,.today-hero .slab-title{color:#fff;font-family:var(--display);font-weight:400;
  text-transform:uppercase;font-size:clamp(50px,15vw,116px);line-height:.78;
  letter-spacing:-.05em;margin:22px 0 0;max-width:11ch}
.today-hero .meta{display:flex;gap:22px;flex-wrap:wrap;margin-top:26px;
  padding-top:18px;border-top:1px solid rgba(255,255,255,.22)}
.today-hero .meta span{font-size:11px;font-weight:700;letter-spacing:.18em;
  text-transform:uppercase;color:rgba(255,255,255,.78)}
.today-hero .meta span b{color:#fff;font-weight:400;font-family:var(--display);
  font-size:17px;letter-spacing:-.02em;margin-right:3px;vertical-align:-1px}
.today-hero .btn{margin-top:30px}
@media(min-width:560px){.today-hero .btn{min-width:280px;width:auto}}

/* ----------------------------------------------------------- the ledger ---- */
/* The list form used everywhere: index numeral in the gutter, content beside
   it, hairline between rows. Exercises, days, blocks and athletes all use it,
   which is what makes the app feel like one book rather than four screens. */
.ledger{list-style:none;margin:0;padding:0}
.ledger > li,.ledger-row{display:grid;grid-template-columns:52px 1fr auto;
  gap:0 16px;align-items:baseline;padding:20px 0 22px;border-top:1px solid var(--line);
  text-align:left;width:100%;background:none;border-left:0;border-right:0;border-bottom:0;
  font-family:var(--sans);color:inherit}
.ledger > li:first-child,.ledger-row:first-child{border-top:0}
.ledger .n{font-family:var(--display);font-weight:400;font-size:clamp(24px,5vw,32px);
  line-height:.8;color:var(--index);letter-spacing:-.04em;grid-row:span 2}
.ledger b{font-family:var(--display);font-weight:400;color:var(--navy);
  font-size:clamp(20px,4.4vw,26px);line-height:.95;letter-spacing:-.03em;
  text-transform:uppercase;display:block}
.ledger .val{font-size:11px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--navy);white-space:nowrap;align-self:center}
.ledger .sub{grid-column:2 / -1;margin-top:8px;font-size:15px;line-height:1.5;color:var(--quiet)}
.ledger .dow,.ledger-row .dow{display:block;font-size:9.5px;font-weight:700;letter-spacing:.2em;
  text-transform:uppercase;color:var(--quiet);margin-bottom:9px}
.ledger-row{cursor:pointer;transition:background var(--fast) var(--ease)}
.ledger-row:hover{background:var(--gray)}

/* days that are done read back, not forward */
.ledger .done b,.ledger-row.done b{color:var(--quiet)}
.ledger-row.rest b{color:var(--quiet);font-family:var(--sans);font-weight:700;
  font-size:clamp(15px,3.4vw,18px);letter-spacing:.02em}
/* Today runs past the text column on both sides. width:auto matters: with the
   inherited width:100% the negative margins shifted the slab left instead of
   widening it, which read as a broken row. */
.ledger-row.today{background:var(--navy);width:auto;
  margin-left:calc(var(--pad) * -1);margin-right:calc(var(--pad) * -1);
  padding-left:var(--pad);padding-right:var(--pad);border-top-color:var(--navy)}
.ledger-row.today b,.ledger-row.today .n{color:#fff}
.ledger-row.today .dow{color:rgba(255,255,255,.72)}
.ledger-row.rest .dow{color:var(--quiet);opacity:.55}
.ledger-row.today .val{color:rgba(255,255,255,.8)}
.ledger-row.today .sub{color:rgba(255,255,255,.72)}

/* On a phone the third column has nowhere to go, so the value drops under the
   title instead of squeezing it into two words per line. */
@media(max-width:560px){
  .ledger > li,.ledger-row{grid-template-columns:44px 1fr;padding:16px 0 18px}
  .ledger .n,.ledger-row .n{font-size:24px}
  .ledger .val,.ledger-row .val{grid-column:2 / -1;margin-top:9px;align-self:start;text-align:left}
  .ledger .sub,.ledger-row .sub{margin-top:6px}
}

/* ------------------------------------------------------- a day's session ---- */
.sess{border-top:1px solid var(--line)}
.sess:first-of-type{border-top:0}
.sess-top{display:block;padding:24px 0 20px;cursor:pointer;
  width:100%;background:none;border:0;text-align:left;font-family:var(--sans);color:inherit}
.sess-t{min-width:0}
.sess-t b{display:block;font-family:var(--display);font-weight:400;
  font-size:clamp(26px,6.4vw,38px);color:var(--navy);line-height:.88;
  text-transform:uppercase;letter-spacing:-.045em}
.sess.done .sess-t b{color:var(--quiet)}
/* One meta row, divided by rules rather than by middle dots. */
.sess-meta{display:flex;flex-wrap:wrap;gap:0;margin-top:16px;
  border-top:1px solid var(--line);padding-top:12px}
.sess-meta > span{font-size:10px;color:var(--quiet);letter-spacing:.2em;
  text-transform:uppercase;font-weight:700;padding-right:18px;margin-right:18px;
  border-right:1px solid var(--line);line-height:1.6}
.sess-meta > span:last-child{padding-right:0;margin-right:0;border-right:0}
.sess-meta .st{color:var(--navy)}
.sess.done .sess-meta .st{color:var(--ok)}
.sess-body{padding:0 0 8px}
.chip{font-size:10px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;
  flex:none;align-self:flex-start;margin-top:5px;padding:5px 9px;border:1px solid currentColor}
.chip-open{color:var(--navy)}
.chip-done{color:var(--ok)}
.chip-skip{color:var(--warn)}

/* what Jack said about today */
.coach-msg{border-left:3px solid var(--navy);padding:4px 0 4px 18px;margin:4px 0 24px}
.coach-msg .label{margin-bottom:8px}
.coach-msg > div:last-child{font-size:17.5px;line-height:1.5;color:var(--navy);
  font-weight:500;max-width:46ch}

/* the exercise ledger */
.items{list-style:none;margin:0;padding:0}
.items li{display:grid;grid-template-columns:auto 1fr auto;gap:0 18px;
  align-items:baseline;padding:22px 0 24px;border-top:1px solid var(--line)}
.items li:first-child{border-top:2px solid var(--rule)}
.items .n{font-family:var(--display);font-weight:400;font-size:clamp(24px,6vw,32px);
  line-height:.8;color:var(--index);letter-spacing:-.05em;min-width:1.9em}
.items .n::after{content:" ·";color:var(--index)}
.items b{font-family:var(--display);font-weight:400;color:var(--navy);
  font-size:clamp(21px,5.2vw,29px);line-height:.92;letter-spacing:-.04em;
  text-transform:uppercase;display:block}
.items .presc{font-size:11px;color:var(--navy);font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;white-space:nowrap;align-self:center}
.items .instr{grid-column:2 / -1;font-size:15px;color:var(--quiet);margin-top:9px;line-height:1.5}
.items .btn-ghost{grid-column:2 / -1;justify-self:start;margin-top:4px}

/* ---- the demo link -------------------------------------------------------
   An athlete taps this mid-workout, on a phone, one-handed. So: a real 44px
   target, a play mark that reads before the words do, and the destination
   named. It is set as a rule rather than a button because the rest of this
   page is hairlines and type, and a filled button here would be the loudest
   thing on a screen whose job is the exercise, not the video. */
.items .demo{
  grid-column:2 / -1; justify-self:start; margin-top:13px;
  display:inline-flex; align-items:center; gap:11px;
  min-height:44px; padding:0 2px 2px 0;
  font-size:11px; font-weight:700; letter-spacing:.16em; text-transform:uppercase;
  color:var(--navy); text-decoration:none;
  border-bottom:2px solid var(--navy);
  transition:color var(--t1) var(--ease), border-color var(--t1) var(--ease);
}
.items .demo-play{
  width:0; height:0; flex:0 0 auto;
  border-left:9px solid currentColor;
  border-top:6px solid transparent; border-bottom:6px solid transparent;
  transition:transform var(--t1) var(--ease);
}
.items .demo-src{ color:var(--index); letter-spacing:.14em; }
.items .demo:hover{ color:var(--blue); border-bottom-color:var(--blue); }
.items .demo:hover .demo-play{ transform:translateX(2px); }
.items .demo:active{ transform:translateY(1px); }
.items .demo:focus-visible{ outline:2px solid var(--navy); outline-offset:4px; }
@media (prefers-reduced-motion:reduce){
  .items .demo, .items .demo-play{ transition:none }
  .items .demo:hover .demo-play{ transform:none }
}

/* A done exercise dims. The demo mark should dim with it rather than sitting
   there as the brightest thing in a finished row. */
.items li[data-done] .demo{ opacity:.45 }

/* ---------------------------------------------------------------- streak ---
   A ledger entry, not a badge. The number carries it, the chain makes it
   physical, and the footnote does the safety work in plain words.

   No confetti, no flame, no gradient. This sits directly under a navy hero on
   a page whose job is the workout, and a kid who trains seriously is not
   flattered by a cartoon. The reward is seeing the row get longer. */
.streak{
  padding:26px 0 24px;
  border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);
  margin:34px 0 0;
}
.streak-head{display:flex;align-items:baseline;justify-content:space-between;gap:16px}
.streak-best{
  font-size:10.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--index);
}
/* At a personal best. The only thing on this block that gets colour, because
   it is the only thing that is only true sometimes. */
.streak-best.is-best{
  color:var(--ok);
  display:inline-flex;align-items:center;gap:7px;
}
.streak-best.is-best::before{
  content:"";width:6px;height:6px;border-radius:50%;background:var(--ok);
  flex:0 0 auto;
}
.streak-count{display:flex;align-items:baseline;gap:14px;margin-top:10px;flex-wrap:wrap}
.streak-n{
  font-family:var(--display);font-weight:400;
  font-size:clamp(46px,13vw,68px);line-height:.82;letter-spacing:-.05em;
  color:var(--navy);
  font-variant-numeric:tabular-nums;
}
.streak-unit{
  font-size:11px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--quiet);
}

/* The chain. Each mark is one assigned session. Filled means done, hollow
   means it was assigned and missed, and the last one can be today: still open,
   drawn as an outline that is waiting rather than a gap that failed. */
.streak-chain{display:flex;align-items:center;gap:7px;margin-top:20px;flex-wrap:wrap}
.streak-chain .link{
  width:13px;height:13px;border-radius:50%;
  background:var(--navy);
  flex:0 0 auto;
}
.streak-chain .link.off{ background:none;box-shadow:inset 0 0 0 1.5px var(--line) }
.streak-chain .link.live{
  background:none;box-shadow:inset 0 0 0 2px var(--navy);
  animation:streak-wait 2.4s var(--ease) infinite;
}
@keyframes streak-wait{ 0%,100%{opacity:1} 50%{opacity:.35} }
@media (prefers-reduced-motion:reduce){ .streak-chain .link.live{animation:none} }

.streak-foot{
  margin:16px 0 0;font-size:12.5px;line-height:1.5;color:var(--index);
  max-width:34em;
}

/* ------------------------------------------------------------ the forms ---- */
/* Underlines, not boxes. A boxed input grid is the other unmistakable tell of
   a generic dashboard, and on a phone the box adds nothing. */
/* Was label.f. A canvas cannot be the target of a <label for>, so the
   signature pad captions itself with a <span class="f"> instead, and this
   rule has to reach it. Every element that matched label.f still matches. */
.f{display:block;font-family:var(--sans);font-size:10.5px;font-weight:700;
  letter-spacing:.18em;text-transform:uppercase;color:var(--quiet);margin:0 0 8px}
input,select,textarea{width:100%;font-family:var(--sans);font-size:17px;color:var(--navy);
  background:transparent;border:0;border-bottom:2px solid var(--line);border-radius:0;
  padding:10px 0;line-height:1.4;min-height:48px;font-weight:500}
input[type=number]{font-family:var(--display);font-weight:400;font-size:30px;letter-spacing:-.02em}
select{padding-right:18px}
textarea{min-height:82px;resize:vertical;font-size:16px}
input:focus,select:focus,textarea:focus{border-bottom-color:var(--navy);outline:none}
input::placeholder,textarea::placeholder{color:var(--line);font-weight:400}
.row2{display:grid;gap:22px}
@media(min-width:520px){.row2{grid-template-columns:1fr 1fr;gap:26px}}
.field{margin-bottom:20px}
input[type=search]{border:2px solid var(--line);padding:10px 12px;font-size:15px}
input[type=search]:focus{border-color:var(--navy)}

/* --------------------------------------------------------- the calendar ----
   A development record. The month is a masthead, the columns are single
   letters, the days are left-aligned in their cell the way entries are
   left-aligned in a log, and every day carries exactly one mark. There are no
   cell borders: the week rules do all the structural work.
   ---------------------------------------------------------------------------- */
.cal-head{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;
  padding:8px 0 20px;border-bottom:3px solid var(--rule);margin-bottom:0}
.cal-title{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
.cal-title b{font-family:var(--display);font-weight:400;font-size:clamp(38px,10vw,64px);
  color:var(--navy);text-transform:uppercase;letter-spacing:-.05em;line-height:.82}
.cal-title span{font-family:var(--sans);font-weight:700;font-size:11px;letter-spacing:.24em;
  color:var(--quiet)}
.cal-nav{display:flex;gap:0;flex:none}
.cal-nav button{width:52px;height:52px;border:0;background:none;color:var(--navy);
  cursor:pointer;display:grid;place-items:center;font-size:22px;
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease)}
.cal-nav button:hover{background:var(--navy);color:#fff}

.cal-grid{display:grid;grid-template-columns:repeat(7,1fr)}
.cal-dow{padding:14px 0 12px;text-align:left;font-size:10px;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:var(--quiet);
  border-bottom:1px solid var(--line)}
.cal-day{background:none;border:0;border-bottom:1px solid var(--line-soft);
  padding:14px 0 16px;min-height:76px;cursor:pointer;font-family:var(--sans);
  display:flex;flex-direction:column;align-items:flex-start;gap:0;
  transition:background var(--t1) var(--ease)}
.cal-day:hover{background:var(--gray)}
.cal-day .d{font-family:var(--display);font-weight:400;font-size:clamp(17px,4.4vw,23px);
  letter-spacing:-.04em;color:var(--navy);line-height:1;
  display:grid;place-items:center;width:1.7em;height:1.7em;margin-left:-.22em}
.cal-day.out .d{color:var(--index);opacity:.5}
.cal-day.today .d{background:var(--navy);color:#fff}
.cal-day.sel .d{box-shadow:inset 0 0 0 2px var(--navy)}
.cal-day.sel.today .d{box-shadow:none}
.dots{display:flex;gap:3px;min-height:18px;align-items:center;margin-top:auto;padding-top:10px}
.mark{font-size:14px;line-height:1;color:var(--navy);font-weight:700}
.mark.done{color:var(--ok)}
.mark.skip{color:var(--warn)}
.mark.rest{color:var(--index)}
.cal-day.out .mark{opacity:.3}

/* The month in one line of numbers. Same data the marks show, counted. */
.cal-tally{display:flex;gap:0;border-top:2px solid var(--rule);margin-top:0}
.cal-tally div{flex:1;padding:18px 0 20px;border-right:1px solid var(--line)}
.cal-tally div:last-child{border-right:0}
.cal-tally b{display:block;font-family:var(--display);font-weight:400;
  font-size:clamp(26px,7vw,38px);line-height:.9;letter-spacing:-.04em;color:var(--navy)}
.cal-tally b.done{color:var(--ok)}
.cal-tally span{display:block;margin-top:10px;font-size:9.5px;font-weight:700;
  letter-spacing:.2em;text-transform:uppercase;color:var(--quiet)}
.cal-legend{display:flex;gap:22px;flex-wrap:wrap;padding:18px 0 0;
  font-size:10px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--quiet)}
.cal-legend b{color:var(--navy);font-weight:700;margin-right:7px}
.cal-legend .done{color:var(--ok)}
.cal-legend .rest{color:var(--index)}
@media(max-width:430px){
  .cal-day{min-height:64px;padding:11px 0 12px}
  .dots{padding-top:8px;min-height:16px}
}

/* --------------------------------------------------- the week, side by side ---- */
/* Jack programs from this: the shape of the week in one look. Seven columns on
   anything wide enough, a scrolling row on a phone. No boxes - the day is
   defined by the rule above it and the space around it. */
.week{display:grid;grid-template-columns:repeat(7,1fr);border-top:2px solid var(--rule)}
.week-day{display:flex;flex-direction:column;align-items:flex-start;gap:0;
  background:none;border:0;border-right:1px solid var(--line);
  padding:16px 12px 20px;min-height:132px;cursor:pointer;text-align:left;
  font-family:var(--sans);transition:background var(--t1) var(--ease)}
.week-day:last-child{border-right:0}
.week-day:hover{background:var(--gray)}
.week-day .wd{font-size:9.5px;font-weight:700;letter-spacing:.2em;text-transform:uppercase;color:var(--quiet)}
.week-day .wn{font-family:var(--display);font-weight:400;font-size:26px;line-height:1;
  letter-spacing:-.03em;color:var(--navy);margin:8px 0 12px}
.week-day .wt{font-size:12.5px;font-weight:700;line-height:1.25;color:var(--navy);
  text-transform:uppercase;letter-spacing:.02em}
.week-day .wm{margin-top:auto;padding-top:12px;font-size:15px;color:var(--navy)}
.week-day.rest .wt,.week-day.rest .wn{color:var(--quiet);font-weight:400}
.week-day.rest .wm{color:var(--line)}
.week-day.today{background:var(--navy)}
.week-day.today .wd{color:rgba(255,255,255,.66)}
.week-day.today .wn,.week-day.today .wt,.week-day.today .wm{color:#fff}
.week-day.sel{box-shadow:inset 0 3px 0 0 var(--navy)}
.week-day.sel.today{box-shadow:inset 0 3px 0 0 #fff}
/* On a phone the whole week fits on one line. It used to be a sideways
   scroller two days wide, so "what does this week look like" took five swipes.
   Seven narrow columns: the day, the date, and a mark. The workout names are
   one tap away, directly underneath, in the selected day. */
@media(max-width:720px){
  .week-day{align-items:center;text-align:center;padding:12px 2px 12px;min-height:0;min-width:0}
  .week-day .wd{font-size:9px;letter-spacing:.12em}
  .week-day .wn{font-size:22px;margin:6px 0 4px}
  .week-day .wt{display:none}
  .week-day .wm{padding-top:2px;font-size:13px}
  .week-day.sel{box-shadow:inset 0 -3px 0 0 var(--navy)}
  .week-day.sel.today{box-shadow:inset 0 -3px 0 0 #fff}
}

/* --------------------------------------------------------- the day editor ----
   Two layouts, both first-class. On a wide screen the index, the fields and the
   controls sit on one line the way they do on a program sheet. On a phone the
   index and the controls take the top line together - which puts the controls
   under the thumb - and the fields get the full width underneath instead of
   being squeezed into a third of it.
   ---------------------------------------------------------------------------- */
.ex-rows{border-top:2px solid var(--rule)}
.ex-row{display:grid;grid-template-columns:auto 1fr;gap:10px 14px;align-items:center;
  padding:16px 0 18px;border-bottom:1px solid var(--line)}
.ex-n{font-family:var(--display);font-weight:400;font-size:26px;line-height:1;
  color:var(--index);letter-spacing:-.05em;grid-row:1}
.ex-acts{display:flex;gap:0;grid-row:1;grid-column:2;justify-self:end}
.ex-fields{display:grid;gap:8px;grid-column:1 / -1;grid-row:2}
.ex-fields input{min-height:46px;padding:8px 0;font-size:16px}
/* Styled by class, not by nth-child. These used to be positional, and adding a
   fourth field to the row silently unstyled it because the input was no longer
   the fourth child once it gained a wrapper. Position is not identity. */
.ex-fields .ex-name{font-family:var(--display);font-weight:400;font-size:20px;
  text-transform:uppercase;letter-spacing:-.03em;border-bottom-width:2px}
.ex-fields .ex-pres{font-size:12px;font-weight:700;letter-spacing:.16em;text-transform:uppercase}
.ex-fields .ex-ins{font-size:15.5px;color:var(--quiet)}

/* ---- the demo link field -------------------------------------------------
   Quieter than the coach note above it: reference material, not instruction.
   The state carries the meaning. Empty is grey, a link we accept goes navy and
   reveals a play mark that opens it, and something we would refuse goes amber
   while it is being typed rather than waiting to fail on save. */
.ex-vid-row{display:flex;align-items:center;gap:4px}
.ex-fields .ex-vid{font-size:14px;color:var(--quiet);flex:1 1 auto;min-width:0}
.ex-fields .ex-vid.has-link{color:var(--navy);border-bottom-color:var(--navy)}
.ex-fields .ex-vid.bad-link{color:var(--warn);border-bottom-color:var(--warn)}
.ex-vid-open{
  flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  width:44px; height:44px;            /* a real target, not a 10px glyph */
  color:var(--blue); text-decoration:none;
  transition:color var(--t1) var(--ease), transform 90ms var(--ease);
}
.ex-vid-open[hidden]{display:none}
.ex-vid-open:hover{color:var(--navy)}
.ex-vid-open:active{transform:scale(.92)}
.ex-vid-open:focus-visible{outline:2px solid var(--navy);outline-offset:2px}
.ex-vid-play{
  width:0;height:0;
  border-left:10px solid currentColor;
  border-top:7px solid transparent;border-bottom:7px solid transparent;
}
@media (prefers-reduced-motion:reduce){ .ex-vid-open{transition:none} }
.ex-acts .btn{min-width:46px;min-height:46px;padding:10px 6px;margin:0;font-size:16px;
  text-decoration:none;color:var(--quiet);border:1px solid transparent}
.ex-acts .btn:hover{color:var(--navy);background:var(--gray)}
.ex-acts .btn:active{transform:scale(.94)}

@media(min-width:700px){
  /* One line: index, fields, controls. */
  .ex-row{grid-template-columns:52px 1fr auto;gap:0 18px;align-items:start;padding:18px 0 20px}
  .ex-n{grid-row:1;font-size:30px;line-height:1.3}
  .ex-acts{grid-row:1;grid-column:3;align-self:start;padding-top:2px}
  .ex-fields{grid-column:2;grid-row:1;gap:4px 18px;grid-template-columns:1fr 200px}
  /* The note and the demo link each run the full width of the field grid;
     the name and the prescription share the top line. By class, because the
     link is a wrapper now and positional selectors stopped matching it. */
  .ex-fields .ex-ins,
  .ex-fields .ex-vid-row{grid-column:1 / -1}
}

/* A row that just moved settles rather than teleporting. */
@media (prefers-reduced-motion: no-preference){
  .ex-row.moved{animation:settle var(--t2) var(--ease) both}
  @keyframes settle{from{background:var(--gray-2)}to{background:transparent}}
}

/* ------------------------------------------------------- the coach table ---- */
.tbl{width:100%;border-collapse:collapse;font-size:14.5px}
.tbl th{text-align:left;font-size:9.5px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--quiet);font-weight:700;padding:0 14px 12px;border-bottom:2px solid var(--rule)}
.tbl th:first-child{padding-left:0}
.tbl td{padding:22px 14px;border-bottom:1px solid var(--line);vertical-align:middle;
  font-size:11px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--quiet)}
.tbl td:first-child{padding-left:0}
.tbl tbody tr{cursor:pointer;transition:background var(--fast) var(--ease)}
.tbl tbody tr:hover{background:var(--gray)}
.tbl .who b{display:block;font-family:var(--display);font-weight:400;
  font-size:clamp(22px,2.6vw,30px);color:var(--navy);text-transform:uppercase;
  letter-spacing:-.032em;line-height:.92}
.tbl .who span{display:block;margin-top:9px;font-size:10.5px;color:var(--quiet);
  letter-spacing:.18em;text-transform:uppercase;font-weight:700}
@media(max-width:680px){
  .tbl thead{display:none}
  .tbl td{display:block;border:0;padding:3px 0}
  .tbl td:empty{display:none}
  .tbl tbody tr{display:block;border-top:1px solid var(--line);padding:20px 0}
  .tbl tbody tr:first-child{border-top:2px solid var(--rule)}
}

/* ------------------------------------------------- notes, errors, empties ---- */
.note{border-left:3px solid var(--navy);padding:4px 0 4px 16px;font-size:15px;margin-bottom:18px;color:var(--navy)}
.err{border-left:3px solid #B4453A;color:#8A3229;padding:4px 0 4px 16px;font-size:15px;
  margin-bottom:18px;font-weight:500}
.ok{border-left:3px solid var(--ok);color:#175139;padding:4px 0 4px 16px;font-size:15px;margin-bottom:18px}
.empty{padding:44px 0;color:var(--quiet);font-size:16px;max-width:44ch}
.empty b{display:block;font-family:var(--display);font-weight:400;
  font-size:clamp(24px,6vw,34px);color:var(--navy);margin-bottom:12px;
  text-transform:uppercase;letter-spacing:-.03em;line-height:.92}
.spin{padding:56px 0;color:var(--quiet);font-size:10.5px;font-weight:700;
  letter-spacing:.2em;text-transform:uppercase}
@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}

/* ===========================================================================
   THE TWO PIECES AT THE TOP OF THE DAY
   ===========================================================================
   These used to be two boxes with the same styling, one after the other, and
   they read exactly like what they were: a quote and another quote. The order
   TODAY → motivation → tip → work is the app's whole opening, so the two have
   to feel like different kinds of speech.

   01 MOTIVATION is a statement. Big, short, room around it. The thing a coach
   writes across the top of the sheet before anyone reads the sheet.

   02 TIP is coaching. A small headline and a couple of sentences with something
   in them you can go and do. It is deliberately quieter - if both shout, the
   first one stops being a statement.

   No cards, no shadows, no rounded corners. Rules and space, the way the rest
   of this product is built.
   =========================================================================== */
.piece{padding:30px 0 26px;border-top:1px solid var(--line)}
.piece:first-of-type{border-top:2px solid var(--rule)}

/* The number is the editorial signal: this is piece one of two, not a card
   that happens to be here. Set as a rule-and-numeral pair with the label. */
.piece-num{display:flex;align-items:baseline;gap:12px;margin-bottom:18px}
.pnum{font-family:var(--display);font-weight:400;font-size:12px;letter-spacing:.06em;
  color:var(--navy);line-height:1}
.piece-num .label{margin:0}
.piece.mine .pnum,.piece.mine .label{color:var(--navy)}

/* --- 01, the statement ---------------------------------------------------- */
/* Sentence case at display size. Archivo Black in uppercase across three lines
   is a wall, not a statement - the weight comes from scale and from the space
   around it, not from shouting. */
.statement{font-family:var(--sans);font-weight:700;
  line-height:1.06;letter-spacing:-.022em;
  color:var(--navy);margin:0 0 4px;text-wrap:balance;
  font-size:clamp(33px,9vw,56px);max-width:15ch}
/* Three registers, chosen by how long the line actually is. A short statement
   gets to be enormous. A long one steps down rather than filling the screen
   with eight lines of navy that nobody finishes. */
.statement.say-big{font-size:clamp(38px,10.6vw,66px);max-width:12ch;line-height:1.02}
.statement.say-mid{font-size:clamp(31px,8.2vw,50px);max-width:16ch;line-height:1.08}
.statement.say-long{font-size:clamp(25px,6.4vw,38px);max-width:22ch;line-height:1.18;
  letter-spacing:-.015em}
.statement-sub{font-size:17px;line-height:1.5;color:var(--ink);max-width:46ch;
  margin:16px 0 0}
.piece-mot{padding-bottom:34px}

/* --- 02, the coaching ----------------------------------------------------- */
/* Deliberately smaller than the statement above it. If both are large, neither
   is the statement, and the reason for having two different pieces disappears.
   The test in regress.mjs holds the gap open. */
.tip-head{font-family:var(--display);font-weight:400;text-transform:uppercase;
  letter-spacing:-.03em;font-size:clamp(20px,4.4vw,28px);line-height:.96;
  color:var(--navy);margin:0 0 12px;max-width:22ch}
.tip-body{font-size:17.5px;line-height:1.55;color:var(--ink);max-width:52ch;margin:0}
/* A tip with no headline still has to look like a tip and not like a small
   statement, so it stays at body size rather than growing. */
.tip-body.tip-only{color:var(--navy);font-weight:500}

/* --- the focus mark, and why -------------------------------------------- */
/* A real button, 44px of it, because it is one. It was tempting to make this a
   caption with a click handler; a caption cannot be reached by keyboard, does
   not announce that it expands, and is too small for a thumb. */
.why{display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap;
  min-height:44px;padding:10px 0;margin-top:22px;
  background:none;border:0;cursor:pointer;text-align:left;color:inherit}
.why-mark{font-family:var(--sans);font-weight:700;font-size:10px;letter-spacing:.24em;
  text-transform:uppercase;color:var(--quiet);line-height:1.4}
.why-hint{font-family:var(--sans);font-weight:700;font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--blue);
  border-bottom:1px solid currentColor;padding-bottom:2px;
  transition:opacity .18s ease}
.why:hover .why-hint{opacity:.68}
.why.open .why-hint{opacity:.45}
.why-body{font-size:15px;line-height:1.55;color:var(--ink);max-width:46ch;
  margin:2px 0 0;padding-left:14px;border-left:2px solid var(--line)}

@media(max-width:400px){
  .piece{padding:26px 0 22px}
  .why{gap:8px}
}

/* A row of facts under a name: level, throws, status. Rules between, no chips. */
.stats{display:flex;flex-wrap:wrap;gap:0 26px;margin-top:22px;padding-top:16px;
  border-top:1px solid var(--line)}
.stats span{font-size:10.5px;font-weight:700;letter-spacing:.18em;
  text-transform:uppercase;color:var(--quiet)}

/* What a training block is for, set as a statement rather than a caption. */
.block-aim{font-family:var(--sans);font-weight:700;font-size:clamp(21px,5vw,30px);
  line-height:1.24;letter-spacing:-.015em;color:var(--navy);max-width:24ch;margin:0}

/* ------------------------------------------------------------ film to Jack ---- */
/* No upload. Nothing is stored here. The athlete sends film to a person. */
.film{background:var(--navy);color:#fff;
  margin-left:calc(50% - 50vw);margin-right:calc(50% - 50vw);
  padding:clamp(30px,6vw,50px) max(var(--pad),calc(50vw - 410px + var(--pad)))}
.film .label{color:rgba(255,255,255,.62)}
.film h2{color:#fff;font-family:var(--display);font-weight:400;text-transform:uppercase;
  letter-spacing:-.045em;font-size:clamp(34px,8.5vw,56px);line-height:.86;margin:18px 0 16px}
.film p{color:rgba(255,255,255,.82);font-size:16.5px;line-height:1.5;max-width:38ch;margin:0}
.film-acts{margin-top:30px;max-width:420px}
/* Inside a sentence, but still the only way to reach the messages app, so it
   gets a real 44px target with a negative margin so the line does not shift. */
.film-alt{margin-top:16px!important;font-size:15px;color:rgba(255,255,255,.66)!important;
  line-height:2.6}
.film-alt a{color:#fff;text-decoration:underline;text-underline-offset:4px;font-weight:600;
  display:inline-flex;align-items:center;min-height:44px;padding:0 6px;margin:0 -6px}

/* --------------------------------------------------------------- install ---- */

/* ------------------------------------------------------------ the ask ---- */
.ask{display:block;border-top:2px solid var(--rule);padding:26px 0 30px;margin:0}
.ask .label{margin-bottom:14px}
.ask h3{font-family:var(--display);font-weight:400;text-transform:uppercase;
  letter-spacing:-.03em;font-size:clamp(24px,5.4vw,32px);line-height:.92;
  color:var(--navy);margin:0 0 12px}
.ask .ask-msg{font-size:16.5px;line-height:1.5;color:var(--ink);max-width:42ch;margin:0}
.ask-row{display:flex;gap:12px;margin-top:24px;flex-wrap:wrap}
.ask-row .btn{flex:1 1 auto;min-width:150px}
@media (prefers-reduced-motion: no-preference){
  .ask{animation:askin var(--t2) var(--ease) both}
  @keyframes askin{from{opacity:0}to{opacity:1}}
}

/* --------------------------------------------------- content library (coach) ---- */
.lib{padding:22px 0 20px;border-top:1px solid var(--line)}
.lib:first-of-type{border-top:0}
.lib .label{margin-bottom:10px}
.lib b{display:block;font-family:var(--display);font-weight:400;color:var(--navy);
  font-size:19px;line-height:1.05;letter-spacing:-.02em;text-transform:uppercase;margin-bottom:8px}
.lib p{margin:0;font-size:16px;line-height:1.5;color:var(--ink);max-width:60ch}
.lib.off{opacity:.45}
.lib.off .label{color:var(--warn)}
.lib-acts{display:flex;gap:18px;margin-top:10px;flex-wrap:wrap}
.lib.flagged{border-left:3px solid var(--warn);padding-left:16px;margin-left:-19px}
.lib-flag{margin:10px 0 0!important;font-size:12px!important;color:var(--warn)!important;
  font-weight:700;letter-spacing:.1em;text-transform:uppercase}
.review-banner{border-top:2px solid var(--warn);padding:26px 0 30px}
.review-banner .label{color:var(--warn);margin-bottom:14px}
.review-banner b{display:block;font-family:var(--display);font-weight:400;color:var(--navy);
  font-size:clamp(24px,4vw,32px);line-height:.94;letter-spacing:-.03em;
  text-transform:uppercase;margin-bottom:12px}
.review-banner p{font-size:16px;line-height:1.5;color:var(--ink);max-width:56ch;margin:0 0 22px}
.hist{padding:16px 0 6px;border-top:1px solid var(--line-soft)}
.hist .label{margin-bottom:8px}
.hist p{margin:0 0 8px;font-size:15px;line-height:1.5;color:var(--ink)}
.hist em{font-style:normal;font-weight:700;color:var(--quiet);font-size:10.5px;
  letter-spacing:.14em;text-transform:uppercase}
.film-link{display:inline-flex;align-items:center;min-height:44px;color:var(--navy);
  font-weight:700;font-size:13px;letter-spacing:.1em;text-transform:uppercase;
  text-decoration:underline;text-underline-offset:4px}
.film-link:hover{color:var(--blue)}
.confirm-row{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:14px;
  border-left:3px solid var(--warn);padding-left:16px;font-size:15px;color:var(--navy)}

/* ------------------------------------------------------------- dialogs ---- */
dialog{border:0;border-top:6px solid var(--navy);padding:0;max-width:520px;width:calc(100% - 32px);
  background:var(--paper);color:var(--ink)}
dialog::backdrop{background:rgba(11,31,58,.55)}

/* A DIALOG ON A PHONE IS A SHEET.
   A centred box floating in the middle of a 390px screen is a desktop pattern
   wearing a phone's clothes: your thumb is at the bottom and the controls are
   in the middle, and the thing underneath is visible all the way round, which
   reads as a web page with an overlay on it rather than as a screen.

   Below 640px every dialog comes up from the bottom edge, full width, with the
   safe area respected so the buttons clear the home indicator. Above it, the
   centred box is right and stays. Same markup either way - this is a layout
   decision, not a second component. */
@media (max-width: 639px) {
  dialog{position:fixed;margin:0;bottom:0;left:0;right:0;top:auto;
    width:100%;max-width:none;max-height:88dvh;overflow:auto;
    border-top:5px solid var(--navy);
    padding-bottom:env(safe-area-inset-bottom)}
  dialog .dlg-in{padding:22px var(--pad) 26px}
  /* The grab edge. Not a decoration: it is the one affordance that says this
     came from the bottom and is dismissible, in a design with no rounded
     corners to say it for us. */
  dialog .dlg-in::before{content:'';display:block;width:40px;height:3px;
    background:var(--line);margin:0 auto 18px}
  .dlg-actions{flex-direction:column-reverse;gap:10px}
  .dlg-actions .btn{width:100%;min-width:0}
}
@media (max-width: 639px) and (prefers-reduced-motion: no-preference) {
  dialog[open]{animation:sheetup var(--t2) var(--ease) both}
}
@keyframes sheetup{from{transform:translateY(14px);opacity:.4}to{transform:none;opacity:1}}
.dlg-in{padding:clamp(24px,5vw,36px)}
.dlg-in h2{font-family:var(--display);font-weight:400;font-size:clamp(24px,5vw,32px);
  letter-spacing:-.03em;text-transform:uppercase;line-height:.94;margin-bottom:10px}
.dlg-actions{display:flex;gap:12px;margin-top:26px;flex-wrap:wrap}
.dlg-actions .btn{flex:1 1 auto;min-width:130px}

/* ================================= auth ================================= */
.auth-wrap{min-height:100dvh;display:flex;flex-direction:column;align-items:center;
  justify-content:center;padding:32px var(--pad);background:var(--paper)}
.auth-card{width:100%;max-width:420px;border-top:6px solid var(--navy);padding:34px 0 0}
.auth-logo{display:flex;align-items:center;gap:12px;margin-bottom:30px}
.auth-logo img{width:40px;height:40px}
.auth-logo b{font-family:var(--sans);font-weight:700;font-size:11px;color:var(--navy);
  text-transform:uppercase;letter-spacing:.22em}
.auth-card h1{font-size:clamp(34px,8vw,46px);margin-bottom:12px;text-align:left}
.auth-sub{font-size:15.5px;color:var(--quiet);text-align:left;margin-bottom:28px;max-width:38ch}
.gbtn{display:flex;align-items:center;justify-content:center;gap:11px;width:100%;
  padding:15px;min-height:52px;border:2px solid var(--navy);background:transparent;
  color:var(--navy);font-family:var(--sans);font-size:13px;font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;cursor:pointer;
  transition:background var(--fast) var(--ease),color var(--fast) var(--ease)}
.gbtn:hover{background:var(--navy);color:#fff}
.gbtn:hover svg path{fill:#fff}
.gbtn svg{width:18px;height:18px;flex:none}
.or{display:flex;align-items:center;gap:14px;margin:24px 0;color:var(--quiet);
  font-size:10px;font-weight:700;letter-spacing:.2em;text-transform:uppercase}
.or::before,.or::after{content:"";flex:1;height:1px;background:var(--line)}
.auth-foot{margin-top:10px;text-align:left;font-size:15px;color:var(--quiet)}
.auth-foot a,.linkish{color:var(--navy);text-decoration:underline;text-underline-offset:4px;
  font-weight:700;background:none;border:0;font-family:var(--sans);font-size:inherit;
  cursor:pointer;display:inline-flex;align-items:center;min-height:44px;padding:0 6px;margin:0 -6px}
.auth-foot a:hover,.linkish:hover{color:var(--blue)}
.forgot-row{display:flex;justify-content:flex-start;margin:-10px 0 6px}


/* ============================================================================
   MOTION
   Everything below is opt-in: an element animates because it was given a class,
   never because of where it sits. All of it is switched off under
   prefers-reduced-motion by the single rule at the end.
   ============================================================================ */

/* A whole view arriving. Used once per navigation, on the container. */
@media (prefers-reduced-motion: no-preference){
  .view-in{animation:viewin var(--t3) var(--ease) both}
  @keyframes viewin{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

  /* One element arriving, optionally after the one above it. Set --d to stagger.
     The stagger is short on purpose: a long one reads as a loading screen. */
  .enter{animation:enter var(--t2) var(--ease) both;animation-delay:var(--d,0ms)}
  @keyframes enter{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

  /* The exercise list. 45ms apart, capped, so a fifteen-item day does not turn
     into a slideshow. */
  .items li{animation:enter var(--t2) var(--ease) both;animation-delay:calc(var(--i,0) * 45ms)}

  dialog[open]{animation:enter var(--t2) var(--ease) both}
  dialog[open]::backdrop{animation:fade var(--t2) var(--ease) both}
  @keyframes fade{from{opacity:0}to{opacity:1}}
}

/* The arrow is part of the sentence: SEND FILM TO JACK -> . It nudges on hover
   because the button is going somewhere, which is the only reason to move. */
.btn-arrow::before{content:"";display:none}
.btn-arrow{gap:12px}
.btn-arrow .arw{display:inline-block;transition:transform var(--t1) var(--ease)}
@media (hover:hover){.btn-arrow:hover .arw{transform:translateX(4px)}}

/* Buttons: a 1px lift on hover, a 1px press on click. Physical, not decorative. */
.btn{position:relative;overflow:hidden}
@media (hover:hover){
  .btn-navy:hover,.btn-out:hover{transform:translateY(-1px)}
}
.btn:active{transform:translateY(1px)}

/* The one flourish in the app: a single pale bar crossing the primary action
   once on hover. Not a gradient, not a loop, not a glow - a lit edge passing
   over the button and stopping. Only .btn-lit gets it, and only one control per
   screen is ever given that class. */
.btn-lit::after{content:"";position:absolute;top:-60%;bottom:-60%;width:36%;
  background:rgba(255,255,255,.17);transform:translateX(-320%) skewX(-18deg);
  pointer-events:none}
.btn-lit.on-paper::after{background:rgba(11,31,58,.10)}
@media (hover:hover) and (prefers-reduced-motion: no-preference){
  .btn-lit:hover::after,.btn-lit:focus-visible::after{animation:sheen var(--t3) var(--ease) 1 both}
  @keyframes sheen{from{transform:translateX(-320%) skewX(-18deg)}
                   to{transform:translateX(420%) skewX(-18deg)}}
}

/* The bottom nav indicator grows in place rather than snapping on. */
.bottomnav button{position:relative}
.bottomnav button::after{content:"";position:absolute;left:0;right:0;bottom:0;height:3px;
  background:currentColor;transform:scaleX(0);transform-origin:center;
  transition:transform var(--t2) var(--ease)}
.bottomnav button[aria-current="page"]::after{transform:scaleX(1)}
@media(min-width:900px){
  .bottomnav button::after{bottom:-2px;height:3px;background:var(--navy)}
}

/* A completed exercise settles rather than blinking. */
.items li{transition:opacity var(--t1) var(--ease)}

/* "Saved." A word, in place, that fades out. Not a toast. */
.saved{display:inline-flex;align-items:center;margin-left:14px;font-size:10.5px;
  font-weight:700;letter-spacing:.2em;text-transform:uppercase;color:var(--ok)}
@media (prefers-reduced-motion: no-preference){
  .saved{animation:enter var(--t1) var(--ease) both}
}

/* Loading is a word, not a spinner. There is nothing to spin about: these
   queries take a few hundred milliseconds. */
.spin{opacity:.55}

@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
  .btn:hover,.btn:active{transform:none!important}
  .bottomnav button[aria-current="page"]::after{transform:scaleX(1)}
}

/* ---------------------------------------------------------- family logins --
   Invited and Active are different facts, so they get different weight. The
   state word is small, uppercase and set in the gutter; the email is the thing
   you actually read. No cards, no pills with rounded corners - a hairline and
   a rule, like everything else. */
.famrow{padding:14px 0;border-top:1px solid var(--line-soft);
        display:grid;grid-template-columns:78px 1fr;gap:2px 14px;align-items:baseline}
.famrow b{grid-column:2;font-size:16px;color:var(--navy);word-break:break-word}
.famrow .muted{grid-column:2;font-size:13.5px}
.stat{grid-row:1 / span 2;grid-column:1;align-self:start;
      font-family:var(--sans);font-size:11px;font-weight:700;letter-spacing:.08em;
      text-transform:uppercase;padding:3px 0;border-top:2px solid;line-height:1.5}
.stat-on{color:var(--navy);border-color:var(--navy)}
.stat-wait{color:var(--quiet);border-color:var(--line)}
.stat-off{color:var(--quiet);border-color:var(--line-soft)}
.flagline{grid-column:2;margin-top:6px;padding:7px 10px;background:var(--gray);
          border-left:2px solid var(--blue);font-size:13px;color:var(--ink)}
.famacts{grid-column:2;margin-top:8px;display:flex;flex-wrap:wrap;gap:2px}
.famacts .btn{padding-left:0;padding-right:16px}
.copyout{grid-column:2;width:100%;font-size:12.5px;padding:8px 10px;
         border:1px solid var(--line);background:var(--gray);color:var(--ink)}
@media(max-width:520px){
  .famrow{grid-template-columns:1fr}
  .famrow b,.famrow .muted,.famacts,.flagline,.copyout{grid-column:1}
  .stat{grid-row:auto;grid-column:1;justify-self:start;margin-bottom:4px}
}

/* ------------------------------------------------------------ going back --
   Editorial, not a button. A rule above it, the label in the same small
   letter-spaced caps as every other label in the product, and a 44px target
   that is invisible until you need it.

   The arrow moves, the text does not: it reads as a direction rather than a
   control that wobbles. Motion is the one shared easing curve, and it stops
   entirely under prefers-reduced-motion because a nudge is decoration, not
   information. */
.backlink{
  display:inline-flex;align-items:center;gap:9px;
  min-height:44px;padding:11px 14px 11px 0;margin:0 0 2px;
  background:none;border:0;cursor:pointer;
  font-family:var(--sans);font-size:12px;font-weight:700;
  letter-spacing:.09em;text-transform:uppercase;color:var(--quiet);
  transition:color var(--t1) var(--ease);
}
.backlink:hover{color:var(--navy)}
.backlink:focus-visible{outline:2px solid var(--blue);outline-offset:3px}
.backlink-arrow{
  display:inline-block;font-size:14px;line-height:1;
  transition:transform var(--t1) var(--ease);
}
.backlink:hover .backlink-arrow{transform:translateX(-3px)}
.backlink:active .backlink-arrow{transform:translateX(-5px)}
@media(prefers-reduced-motion:reduce){
  .backlink,.backlink-arrow{transition:none}
  .backlink:hover .backlink-arrow,.backlink:active .backlink-arrow{transform:none}
}

/* ------------------------------------------------------ exercise library --
   The row is the pick target; edit and remove sit under it, quiet, so the
   common action stays the obvious one. */
.exlib-row{border-bottom:1px solid var(--line-soft)}

/* ---- "this one has a demo" ----------------------------------------------
   A play mark, sized to sit on the baseline of the exercise name without
   pushing the row taller. It is a mark, not a badge: the library is a list to
   scan, and a filled pill on every other line would turn scanning into
   reading. */
.has-demo{
  display:inline-block; margin-left:9px; vertical-align:baseline;
  width:0; height:0;
  border-left:7px solid var(--blue);
  border-top:5px solid transparent; border-bottom:5px solid transparent;
}
.ledger-row:hover .has-demo{ border-left-color:var(--navy) }


.exlib-row .ledger-row{border-bottom:0}
/* The scrolling library list.
   .btn-ghost carries margin:0 -6px so its text optically aligns with the
   content around it rather than its own padding box. Inside a scroller that
   6px hangs outside the clip rectangle, and the "E" of EDIT was being sliced
   off. Pulling the box left by 6 and padding it back gives the overhang room
   without moving a single pixel of content. */
.exlib-list{
  max-height:52vh; overflow:auto; margin-top:18px;
  margin-left:-6px; padding-left:6px;
  margin-right:-6px; padding-right:6px;
}
.exlib-acts{display:flex;gap:2px;padding:0 0 10px}
.exlib-acts .btn{padding:6px 14px 6px 0;font-size:12px}

/* ------------------------------------------------------------ ticking off --
   The index number IS the tick. Tapping it turns 01 into a check, so nothing
   new appears in the row and the ledger keeps its shape. A done line goes
   quiet rather than getting a badge: the eye should be drawn to what is left,
   not to what is finished. */
.items-tally{font-family:var(--sans);font-size:12px;font-weight:700;letter-spacing:.09em;
             text-transform:uppercase;color:var(--quiet);margin:0 0 6px;
             font-variant-numeric:tabular-nums;transition:color var(--t2) var(--ease)}
.items-tally.all{color:var(--navy)}
.items .n.tick{
  -webkit-appearance:none;appearance:none;background:none;border:0;cursor:pointer;
  min-width:44px;min-height:44px;margin-left:-10px;padding:0 10px;
  text-align:left;font:inherit;
  /* Not --index. The ghosted numeral colour is fine for furniture nobody can
     touch, but the moment this became a button it became a control, and a
     control has to be readable: --index measures 3.36:1 on white and fails AA
     at this size. --quiet is 5.23:1. Caught by the contrast test, not by eye. */
  color:var(--quiet);
  transition:color var(--t1) var(--ease),transform var(--t1) var(--ease);
}
.items .n.tick:hover{color:var(--navy)}
.items .n.tick:active{transform:scale(.9)}
.items .n.tick:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
.items li.ticked .n.tick{color:var(--navy)}
.items li.ticked b{color:var(--quiet);text-decoration:line-through;
                   text-decoration-thickness:1px;text-underline-offset:2px}
.items li.ticked .presc,.items li.ticked .instr{color:var(--index)}
.items li{transition:opacity var(--t2) var(--ease)}
.items li.ticked{opacity:.72}
@media(prefers-reduced-motion:reduce){
  .items li,.items .n.tick,.items-tally{transition:none}
  .items .n.tick:active{transform:none}
}

/* A number that changes should be seen to change. One short rise, no bounce,
   no colour flash - the same restraint as the rest of the motion system. */
.today-hero .tally{display:inline-block;font-variant-numeric:tabular-nums}
.tally.bump{animation:tally-bump var(--t2) var(--ease)}
@keyframes tally-bump{from{transform:translateY(4px);opacity:.5}to{transform:none;opacity:1}}
@media(prefers-reduced-motion:reduce){.tally.bump{animation:none}}

/* ===========================================================================
   MAKE IT AN APP
   ===========================================================================
   The install offer, given the same treatment as everything else on this
   screen: a rule, a label, a statement, an action. It sits at the END of Today,
   after the work, because it used to sit at the top and the thing it was
   pushing below the fold was the briefing - the entire reason the screen
   exists.

   Navy on paper rather than a tinted box. No card, no border-radius, no icon
   in a circle. It should read as the last thing Jack says on the page, not as
   a browser notification that wandered in.
   =========================================================================== */
.getapp{padding:34px 0 30px;margin-top:8px}
.getapp-rule{height:3px;background:var(--rule);margin-bottom:22px}
.getapp .label{margin-bottom:12px}
.getapp-say{font-family:var(--display);font-weight:400;text-transform:uppercase;
  letter-spacing:-.03em;line-height:.94;color:var(--navy);
  font-size:clamp(28px,7.4vw,46px);max-width:13ch;margin:0 0 14px}
.getapp-sub{font-size:16.5px;line-height:1.55;color:var(--ink);max-width:46ch;margin:0 0 20px}

/* The iPhone route is three real steps and people lose it at the second one,
   so they are numbered and set as a list rather than run together in a
   sentence. The numerals are the same editorial numerals as 01 and 02 above. */
.getapp-steps{list-style:none;margin:0 0 8px;padding:0;counter-reset:step}
.getapp-steps li{counter-increment:step;position:relative;
  padding:12px 0 12px 40px;border-top:1px solid var(--line-soft);
  font-size:16px;line-height:1.45;color:var(--ink)}
.getapp-steps li::before{content:counter(step,decimal-leading-zero);
  position:absolute;left:0;top:13px;
  font-family:var(--display);font-weight:400;font-size:12px;
  letter-spacing:.06em;color:var(--navy)}

/* Declining is allowed and should look allowed - a plain, quiet, full-size
   target, not a grey X in a corner that people hit by accident. */
.getapp-after{font-size:15.5px;line-height:1.55;color:var(--ink);max-width:46ch;
  margin:16px 0 0}
.getapp-after:empty{display:none}
.getapp-no{display:block;width:100%;min-height:44px;margin-top:16px;
  background:none;border:0;padding:12px 0;cursor:pointer;text-align:left;
  font-family:var(--sans);font-weight:700;font-size:10px;letter-spacing:.22em;
  text-transform:uppercase;color:var(--quiet)}
.getapp-no:hover{color:var(--ink)}

/* ===========================================================================
   TOUCH
   ===========================================================================
   Measured, not guessed: app/tools/measure.mjs reported every interactive
   control on the client screen carrying touch-action:auto and the browser's
   default tap highlight. Two consequences on a phone, both of which read as
   "this is a website in an app costume" rather than as an app:

   1. touch-action:auto leaves double-tap-to-zoom on the table, so Safari waits
      to find out whether a second tap is coming before letting the first one
      through. The button is not slow. The browser is being polite.

   2. The default tap highlight paints a translucent grey rectangle over the
      element on press. It ignores the shape of the design, it is the wrong
      colour, and it arrives at a different moment from our own :active state,
      so a press looks like two separate things happening.

   The :active transforms this replaces them with were already here and already
   correct. This is about getting out of their way.

   Scrolling is untouched. touch-action:manipulation removes double-tap zoom and
   nothing else - panning, flicking and pinch-to-zoom on the page all behave
   exactly as before, and it is applied to controls rather than to containers.
   =========================================================================== */
button, a, summary, label, input[type="checkbox"], input[type="radio"],
select, .tab, .btn, .n.tick, .backlink, #nav button {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* The Drills tab. "No demo yet" is quiet on purpose: it is a to-do list for
   Jack's filming, not an error. */
.drill-row{display:grid;grid-template-columns:1fr auto;column-gap:14px;align-items:center;
  border-bottom:1px solid var(--line-soft);padding:4px 0}
.drill-main{grid-column:1;grid-row:1 / span 2;background:none;border:0;padding:12px 0;text-align:left;
  cursor:pointer;font:inherit;color:var(--navy);display:flex;flex-direction:column;gap:3px;min-height:52px}
.drill-main b{font-size:16px}
.drill-rx{font-size:13px;color:var(--quiet)}
.drill-state{grid-column:2;grid-row:1;align-self:end;background:none;border:0;cursor:pointer;padding:8px 0 2px;
  font-family:var(--sans);font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--navy);text-decoration:underline;text-underline-offset:4px;white-space:nowrap;
  display:inline-flex;align-items:center;gap:7px;justify-content:flex-end}
.drill-state.on{color:var(--quiet);text-decoration:none}
.drill-remove{grid-column:2;grid-row:2;align-self:start;justify-self:end;background:none;border:0;
  cursor:pointer;padding:4px 0 8px;font-family:var(--sans);font-size:11px;color:var(--quiet);
  text-decoration:underline;text-underline-offset:3px}
.drill-remove:hover{color:#8A3229}
.drill-main:hover b,.drill-state:hover{color:var(--blue)}
.drill-tools{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;margin:14px 0 4px}
.drill-tools input{flex:1 1 220px}
.drill-tools select{flex:0 0 auto;width:auto;min-width:170px}

/* ---- the roster -------------------------------------------------------- */
.roster{list-style:none;margin:0;padding:0;border-top:3px solid var(--rule)}
.roster li{border-bottom:1px solid var(--line)}
.roster-row{width:100%;display:grid;grid-template-columns:1fr auto 14px;align-items:center;gap:14px;
  background:none;border:0;padding:18px 0;text-align:left;cursor:pointer;font:inherit;color:var(--navy);
  transition:background var(--t1) var(--ease)}
.roster-row:hover{background:var(--gray)}
.roster-row:active{background:var(--gray-2)}
.roster-main{display:flex;flex-direction:column;gap:4px;min-width:0}
.roster-main b{font-family:var(--display);font-weight:400;font-size:21px;letter-spacing:-.02em;
  text-transform:uppercase;line-height:1.05}
.roster-sub{font-size:13px;color:var(--quiet)}
.roster-flag{font-size:12px;font-weight:600;color:var(--warn);margin-top:2px}
.roster-state{font-size:10.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  white-space:nowrap;padding:6px 9px;border:1.5px solid currentColor;line-height:1}
.roster-state.st-todo{color:var(--navy)}
.roster-state.st-done{color:var(--ok)}
.roster-state.st-skip{color:var(--warn)}
.roster-state.st-rest{color:var(--quiet);border-color:transparent;padding-left:0;padding-right:0}
.roster-go{font-size:22px;color:var(--index);line-height:1}

/* ---- athlete file sections -------------------------------------------- */
/* Sticks under the app bar so the three sections stay one tap away however
   far down the plan you are. Underline tabs, not pills: this is a page with
   parts, not a toggle. */
.seg{position:sticky;top:calc(58px + env(safe-area-inset-top));z-index:40;background:var(--paper);
  display:grid;grid-template-columns:repeat(3,auto);justify-content:start;gap:22px;
  margin:18px 0 0;border-bottom:1px solid var(--line)}
.seg-b{background:none;border:0;border-bottom:3px solid transparent;margin-bottom:-1px;cursor:pointer;
  padding:14px 0 11px;min-height:48px;font-family:var(--sans);font-size:11.5px;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--quiet);white-space:nowrap;position:relative;
  transition:color var(--t1) var(--ease),border-color var(--t1) var(--ease)}
.seg-b:hover{color:var(--navy)}
.seg-b[aria-selected="true"]{color:var(--navy);border-bottom-color:var(--navy)}
.seg-flag::after{content:"";position:absolute;top:12px;right:-9px;width:6px;height:6px;border-radius:50%;
  background:var(--warn)}
.pane > .sec:first-child,.pane > .today-hero:first-child{margin-top:18px}
@media (max-width:380px){ .seg{gap:16px} .seg-b{font-size:10.5px;letter-spacing:.1em} }

/* ---- day editor: the save bar ----------------------------------------- */
/* Pinned just above the bottom bar, so Save is under the thumb however long the
   day gets. The state line says, in words, whether anything is unsaved. */
.ex-add{display:flex;gap:18px;align-items:center;flex-wrap:wrap;margin-top:18px}
.savebar{position:sticky;bottom:calc(58px + env(safe-area-inset-bottom));z-index:30;
  background:var(--paper);border-top:2px solid var(--rule);margin:30px calc(-1 * var(--pad)) 0;
  padding:12px var(--pad)}
.savebar-in{display:flex;align-items:center;gap:10px}
.savebar-in .btn{min-height:48px;padding:12px 18px}
.savebar-state{flex:1;font-size:12.5px;color:var(--quiet);line-height:1.3}
.savebar-state.is-dirty{color:var(--warn);font-weight:600}
.savebar-state.is-saved{color:var(--ok);font-weight:600}
.savebar-ask{flex-wrap:wrap;margin:0 0 12px;gap:8px}
.savebar-q{flex:1 1 100%;font-weight:600;color:var(--navy)}
@media(min-width:900px){ .savebar{bottom:0} }

/* Rows of text actions (Resend / Copy link / Revoke). .btn-ghost carries a
   -6px optical margin for when it stands alone; side by side that margin ate
   the space between words and they read as one long label. */
.famacts,.exlib-acts{gap:20px}
.famacts .btn,.exlib-acts .btn{margin:0;padding-left:0;padding-right:0}

/* ---- ticking a drill off ---------------------------------------------- */
/* A circle you can see is a control. The bare ghosted numeral it replaces was
   the right look and the wrong affordance: nobody knew it could be tapped. */
.items li:has(.n.tick){align-items:start}
.items .n.tick{width:44px;height:44px;min-width:44px;min-height:44px;margin:0;padding:0;
  border:1.5px solid var(--navy);border-radius:50%;display:inline-flex;align-items:center;
  justify-content:center;font-family:var(--sans);font-size:12.5px;font-weight:700;letter-spacing:0;
  line-height:1;color:var(--navy);text-align:center;
  transition:background var(--t1) var(--ease),color var(--t1) var(--ease),transform var(--t1) var(--ease)}
.items .n.tick::after{content:none}
.items .n.tick:hover{background:var(--gray)}
.items li.ticked .n.tick{background:var(--navy);color:#fff;font-size:17px}
.items li.tickable{cursor:pointer;-webkit-tap-highlight-color:transparent}
.items li.tickable:active{background:var(--gray)}
.items-tally.all{color:var(--ok)}

/* ---- tap-a-number scales (effort, soreness) --------------------------- */
.scale-field{margin-bottom:26px}
.scale-head{display:flex;justify-content:space-between;align-items:baseline}
.scale-head .f{margin:0 0 10px}
.scale-val{font-family:var(--display);font-size:22px;color:var(--navy);line-height:1;min-width:1ch}
.scale{display:grid;grid-template-columns:repeat(var(--n),1fr);border:1.5px solid var(--navy)}
.scale-b{background:none;border:0;border-left:1px solid var(--line);min-height:46px;padding:0;
  font-family:var(--sans);font-size:14px;font-weight:600;color:var(--navy);cursor:pointer;
  font-variant-numeric:tabular-nums;transition:background var(--t1) var(--ease),color var(--t1) var(--ease)}
.scale-b:first-child{border-left:0}
.scale-b:hover{background:var(--gray)}
.scale-b[aria-checked="true"]{background:var(--navy);color:#fff}
.scale-b:focus-visible{outline:2px solid var(--blue);outline-offset:-3px}
.scale-ends{display:flex;justify-content:space-between;margin-top:7px;font-size:11.5px;color:var(--quiet);
  letter-spacing:.04em}

/* ---- getting started --------------------------------------------------- */
.setup-card{border:2px solid var(--navy);padding:18px 18px 20px;margin:20px 0 4px}
.setup-card .kicker{font-size:10.5px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;
  color:var(--warn);margin-bottom:8px}
.setup-card p{margin:0 0 16px;font-size:16px;line-height:1.45;color:var(--ink)}
.next-steps{list-style:none;margin:22px 0 24px;padding:0;border-top:2px solid var(--rule);counter-reset:st}
.next-steps li{counter-increment:st;display:grid;grid-template-columns:34px 1fr;row-gap:2px;
  padding:14px 0;border-bottom:1px solid var(--line)}
.next-steps li::before{content:counter(st,decimal-leading-zero);grid-row:1 / span 2;
  font-family:var(--display);font-size:18px;color:var(--index);line-height:1.1}
.next-steps li.done::before{content:"\2713";color:var(--ok)}
.next-steps b{font-size:15.5px;color:var(--navy)}
.next-steps span{font-size:13px;color:var(--quiet)}
.next-steps li.done b{color:var(--quiet)}
.prog-bar{height:4px;background:rgba(255,255,255,.18);margin-top:18px;overflow:hidden}
.prog-bar span{display:block;height:100%;background:#fff;transition:width var(--t3) var(--ease)}

/* ---- deleting an athlete ----------------------------------------------- */
.del-sec{margin-top:26px}
.del-box{border-left:3px solid #B4453A;padding:4px 0 4px 16px;margin-top:6px}
.del-box > b{display:block;color:#8A3229;font-size:15px;margin-bottom:8px}
.del-list{margin:0;padding-left:18px;color:var(--ink);font-size:14.5px;line-height:1.5}
.del-list li{margin:4px 0}
.del-go{background:#8A3229;color:#fff;border-color:#8A3229}
.del-go:hover{background:#6F2820;border-color:#6F2820}
.del-go[disabled]{background:var(--gray-2);border-color:var(--gray-2);color:var(--quiet);cursor:not-allowed}
.flash{margin:14px 0 0;padding:10px 14px;border-left:3px solid var(--ok);color:var(--ok);font-weight:600;font-size:14.5px}

/* ---- signed agreements -------------------------------------------------- */
.sigrow{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;align-items:baseline;
  padding:16px 0;border-top:1px solid var(--line-soft)}
.sigrow b{font-size:16px}
.sigrow .muted{grid-column:2;font-size:13px}
.sigrow .famacts{grid-column:2}
.sig-none{border-left:3px solid var(--warn);padding:2px 0 2px 16px;margin-top:8px}
.sig-none b{display:block;color:var(--warn);font-size:15px}
.sig-none p{margin:6px 0 0;font-size:14.5px;color:var(--ink);line-height:1.5}
.sigshot{border:1px solid var(--line);padding:10px;margin:8px 0 22px;background:#fff;min-height:80px;
  display:flex;align-items:center;justify-content:center}
.sigshot img{max-width:100%;height:auto;display:block}
.recline{display:grid;grid-template-columns:1fr;gap:2px;padding:10px 0;border-top:1px solid var(--line-soft)}
.reck{font-size:10.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--quiet)}
.recv{font-size:15.5px;color:var(--navy);word-break:break-word}
.acklist{list-style:none;margin:8px 0 0;padding:0}
.acklist li{display:grid;grid-template-columns:22px 1fr;gap:10px;padding:8px 0;font-size:14.5px;color:var(--ink)}
.acklist li.on .ackmark{color:var(--ok)}
.acklist li.off .ackmark{color:#B4453A}
.ackmark{font-size:15px;line-height:1.3}
@media(min-width:560px){ .recline{grid-template-columns:200px 1fr;gap:14px;align-items:baseline} }

/* ---- needs you ---------------------------------------------------------- */
/* The short list of things that will bite if nobody looks. It is absent when
   there is nothing in it, so its presence is the signal. */
.needs{border-left:3px solid var(--warn);padding:2px 0 2px 16px;margin:20px 0 4px}
.needs .label{color:var(--warn);margin-bottom:8px}
.needs-line{padding:6px 0;font-size:14.5px;line-height:1.5}
.needs-what{display:block;font-weight:700;color:var(--navy);font-size:13px}
.needs-who{color:var(--ink)}
.needs-name{background:none;border:0;padding:2px 0;margin:0;font:inherit;color:var(--navy);
  text-decoration:underline;text-underline-offset:3px;cursor:pointer}
.needs-name:hover{color:var(--blue)}

/* A one-word tab ("Plan") was 38px wide: under the 44px a thumb needs. The
   audit in app/tests/ui/controls.mjs fails if any control drops below it. */
.seg-b{min-width:44px}
.ex-acts .btn[disabled]{opacity:.3;cursor:default}

/* ============================================================================
   NATIVE FEEL  (24 Sep 2026)
   The things that make a page in a browser feel like an app on a phone. None
   of them is decoration. Each one removes a tell that says "website".
   ============================================================================ */

/* No rubber-band. Pulling past the top of a native app shows the app; pulling
   past the top of a website shows the browser's grey and a refresh spinner. */
html{overscroll-behavior:none;color-scheme:light;height:100%}
body{overscroll-behavior-y:none;-webkit-touch-callout:none;min-height:100%}

/* Chrome is not text. Long-pressing a tab bar in a native app does nothing;
   in a website it selects the word and offers to copy it. */
.appbar,.bottomnav,.seg,.btn,.backlink,.roster-row,.drill-row,.week-day,.cal-day,
.items .n.tick,.scale-b,.needs-name,.sigrow .famacts,#nav{
  -webkit-user-select:none;user-select:none}

/* Every surface you can press, presses. The same 140ms, the same curve, the
   same 2% shrink everywhere, so the whole app answers the thumb one way. */
.btn,.roster-row,.drill-main,.drill-state,.ledger-row,.week-day,.cal-day,.items li.tickable,
.needs-name,.seg-b,#nav button,.scale-b,.sess-top,.backlink,.appbar-out,.exlib-row .ledger-row{
  transition:transform var(--t1) var(--ease),background var(--t1) var(--ease),
             color var(--t1) var(--ease),opacity var(--t1) var(--ease)}
.btn:active,.roster-row:active,.drill-main:active,.ledger-row:active,.week-day:active,
.cal-day:active,.needs-name:active,.sess-top:active,.exlib-row .ledger-row:active{
  transform:scale(.98)}
.btn-navy:active{opacity:.85}
#nav button:active,.seg-b:active{opacity:.6}

/* The bar at the bottom is glass, the way the system's own tab bar is glass. */
@media(max-width:899px){
  .bottomnav{background:rgba(11,31,58,.94);
    -webkit-backdrop-filter:saturate(180%) blur(16px);backdrop-filter:saturate(180%) blur(16px);
    box-shadow:0 -1px 0 rgba(255,255,255,.06)}
}

/* Loading is a shape, not a word. Three bars where the content will be, with
   a light moving across them. The word stays for screen readers. */
.spin{position:relative;color:transparent;padding:26px 0 0;min-height:160px}
.spin::before,.spin::after{content:"";display:block;height:14px;border-radius:3px;
  background:linear-gradient(90deg,var(--gray) 0%,var(--gray-2) 45%,var(--gray) 90%);
  background-size:220% 100%}
.spin::before{width:62%;height:36px;margin-bottom:18px}
.spin::after{width:88%;box-shadow:0 30px 0 0 var(--gray),0 60px 0 0 var(--gray)}
@media (prefers-reduced-motion: no-preference){
  .spin::before,.spin::after{animation:shimmer 1.3s linear infinite}
  @keyframes shimmer{from{background-position:120% 0}to{background-position:-100% 0}}
}

/* Installed to the home screen: the status bar is ours, so the bar reaches up
   under it, and the offer to install has nothing left to offer. */
@media (display-mode: standalone){
  .appbar{padding-top:max(env(safe-area-inset-top),0px)}
  .getapp{display:none!important}
}

/* Inputs at 17px already, which is what stops iOS zooming the page when one is
   focused. This makes sure nothing ever dips below the threshold. */
input,select,textarea{font-size:max(17px,1em)}

/* Direction-aware screen changes. See viewIn() in app.js. */
@media (prefers-reduced-motion: no-preference){
  .view-fwd{animation:vfwd var(--t3) var(--ease) both}
  @keyframes vfwd{from{opacity:0;transform:translateX(26px)}to{opacity:1;transform:none}}
  .view-back{animation:vback var(--t3) var(--ease) both}
  @keyframes vback{from{opacity:0;transform:translateX(-20px)}to{opacity:1;transform:none}}
  .view-tab{animation:vtab var(--t2) var(--ease) both}
  @keyframes vtab{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
}

/* The condensing title. The brand mark gives way to the name of the screen
   once its big title has scrolled off, so you always know where you are
   without the title taking a third of the phone to say so. */
.appbar-in{position:relative}
.appbar-title{position:absolute;left:50%;top:50%;transform:translate(-50%,calc(-50% + 8px));
  opacity:0;pointer-events:none;white-space:nowrap;max-width:56vw;overflow:hidden;text-overflow:ellipsis;
  font-family:var(--sans);font-weight:700;font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:#fff;
  transition:opacity var(--t2) var(--ease),transform var(--t2) var(--ease)}
.appbar .brand{transition:opacity var(--t2) var(--ease),transform var(--t2) var(--ease)}
.appbar.condensed .appbar-title{opacity:1;transform:translate(-50%,-50%)}
.appbar.condensed .brand{opacity:0;transform:translateY(-6px)}

/* ============================================================================
   THE PREMIUM CUT  (25 Sep 2026)
   The same book, printed properly. Everything above still holds: rules not
   cards, navy in slabs, numbers as furniture. What changes is the voice and
   the scale. Four faces now, each with one job, the same cast as the site:

     POSTER   Archivo variable, 900, condensed to 74%. Titles. As big as the
              screen allows and then a little more. Never letterspaced.
     EDITOR   Fraunces italic. The one line on a screen that reads as a voice:
              what today is for, what a block is about, the question under a
              form. Never a label, never a button.
     METER    Plex Mono. Dates, counts, indices, the line along the top of a
              screen. Tabular, quiet, always uppercase.
     BODY     Archivo 400 to 500, unchanged.

   The seam, the brand's one red, is a hairline and only a hairline: under a
   title, across the top of the bar, as the dot on today. Never a fill.

   Motion gets a second curve: --pop overshoots a little, for the things that
   answer the thumb (a tick, the tab indicator). Screens arrive in a cascade,
   each block 40ms after the one above it, so a screen unfolds instead of
   flashing on. All of it is off under reduced motion, by the rule above.
   ============================================================================ */
:root{
  --seam:#B5322A;
  --poster:"Archivo","Archivo Black","Arial Black",Impact,sans-serif;
  --editor:"Fraunces","Iowan Old Style","Palatino Linotype",Georgia,serif;
  --meter:"Plex Mono","IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --ease:cubic-bezier(.22,1,.36,1);
  --pop:cubic-bezier(.34,1.4,.64,1);
  --t3:520ms;
}

/* ---- the poster voice --------------------------------------------------- */
h1,.brief-word,.today-hero h1,.today-hero .slab-title,.cal-title b,.film h2,
.ledger b,.items b,.week-day .wn,.roster-name,.cal-tally b,.streak-n{
  font-family:var(--poster);font-weight:900;font-stretch:74%;
  font-variation-settings:"wdth" 74;letter-spacing:-.02em}
h1{font-size:clamp(58px,15vw,124px);line-height:.82;
   /* the seam under a title: the brand's signature, drawn once per screen */
   padding-bottom:14px;position:relative}
h1::after{content:"";position:absolute;left:2px;bottom:0;width:44px;height:3px;background:var(--seam);
  transform-origin:left;transform:scaleX(1)}
.screen-head{padding-top:30px;margin-bottom:22px}
.screen-head h1{line-height:.82}
.screen-head .count{margin-top:14px}
.brief-word{font-size:clamp(64px,19vw,140px);line-height:.78;letter-spacing:-.03em}
.today-hero h1,.today-hero .slab-title{font-size:clamp(58px,17vw,132px);line-height:.8;
  padding-bottom:0;max-width:none;
  /* a shallow extrusion, the price on the site: one face, some depth */
  text-shadow:1px 1px 0 var(--navy-mid),2px 2px 0 var(--navy-mid),3px 3px 0 var(--navy-mid),4px 4px 0 var(--navy-mid)}
.today-hero h1::after,.today-hero .slab-title::after{content:none}
.today-hero{position:relative;overflow:hidden;
  padding-top:clamp(38px,9vw,74px);padding-bottom:clamp(34px,7vw,60px)}
/* one soft light on the slab, top left, where a lamp would be */
.today-hero::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(120% 80% at 0% 0%,rgba(255,255,255,.10),transparent 60%)}
.today-hero > *{position:relative}
.today-hero .kicker{font-family:var(--meter);font-weight:400;font-size:11px;letter-spacing:.2em;
  color:rgba(255,255,255,.62)}
.today-hero .kicker::after{background:rgba(255,255,255,.2)}
.today-hero .meta{margin-top:30px;padding-top:20px;gap:26px}
.today-hero .meta span{font-family:var(--meter);font-weight:400;font-size:11px;letter-spacing:.16em}
.today-hero .meta span b{font-family:var(--poster);font-weight:900;font-stretch:80%;font-size:22px;
  letter-spacing:-.02em;margin-right:6px;vertical-align:-2px}
.today-hero .btn{margin-top:34px;min-height:58px;font-size:13px}

.ledger b{font-size:clamp(24px,5.6vw,32px);line-height:.92}
.ledger .n,.items .n{font-family:var(--poster);font-weight:900;font-stretch:74%;
  font-size:clamp(30px,7vw,42px);color:transparent;-webkit-text-stroke:1.5px var(--index);
  letter-spacing:-.02em}
.ledger-row.today .n{-webkit-text-stroke:1.5px rgba(255,255,255,.7)}
.items .n::after{content:none}
.items b{font-size:clamp(22px,5.4vw,30px);line-height:.94}
.items li{padding:22px 0 24px}
.cal-title b{font-size:clamp(46px,13vw,84px);line-height:.8}
.cal-day .d{font-family:var(--poster);font-weight:800;font-stretch:80%;font-size:clamp(18px,4.8vw,24px)}
.week-day .wn{font-size:30px;font-weight:900}
.cal-tally b{font-size:clamp(34px,9vw,54px);line-height:.8;letter-spacing:-.03em}
.streak-n{font-size:clamp(84px,24vw,160px);line-height:.72;letter-spacing:-.04em;color:var(--navy)}
.film h2{font-size:clamp(36px,9vw,64px);line-height:.85;letter-spacing:-.02em;max-width:12ch}

/* ---- the meter voice ---------------------------------------------------- */
.label,.count,h2,.brief-date,.items .presc,.ledger .val,.ledger .dow,.week-day .wd,.cal-dow,
.cal-title span,.cal-tally span,.roster-state,.appbar b,.top-brand b,.top-who,.appbar-title,
.items-tally,.streak-unit,.needs-what,.savebar-state,.seg-b,.cal-legend,.drill-state,.sigrow .muted,.roster-sub{
  font-family:var(--meter);font-weight:400}
.label,.count{font-size:10.5px;letter-spacing:.22em}
h2{font-size:11px;letter-spacing:.2em;font-weight:400}
.count{display:flex;align-items:center;gap:12px}
.count::before{content:"";width:18px;height:1px;background:var(--seam);flex:none}
.seg-b{font-weight:400;letter-spacing:.16em;font-size:11px}
.roster-state{letter-spacing:.14em;font-size:10.5px}

/* ---- the editor voice --------------------------------------------------- */
/* Statements. The line that says what a thing is for. Set as a voice, not a
   caption: Fraunces italic, a size up from body, tight and dark. */
.hint,.film p,.brief .line,.needs-line .needs-who,.savebar-q,.empty p,.sec > p:first-of-type,
.block-goal,.today-hero .lede,.fam-note{
  font-family:var(--editor);font-style:italic;font-weight:420;
  font-variation-settings:"opsz" 18,"SOFT" 30;letter-spacing:-.005em}
.hint{font-size:16px;line-height:1.45;color:var(--quiet);max-width:52ch}
.film p{font-size:clamp(19px,4.8vw,24px);line-height:1.3;color:rgba(255,255,255,.88);max-width:30ch}
.savebar-q{font-size:17px}
.empty p{font-size:19px;line-height:1.35;color:var(--navy)}

/* ---- the bars ----------------------------------------------------------- */
.appbar{box-shadow:inset 0 3px 0 var(--seam)}
.appbar-in{height:58px}
.appbar b{font-family:var(--poster);font-weight:900;font-stretch:76%;font-size:13px;letter-spacing:.06em}
.appbar-title{font-family:var(--poster);font-weight:900;font-stretch:76%;font-size:14px;letter-spacing:.04em}
.appbar-out{font-family:var(--meter);font-weight:400;font-size:10.5px;letter-spacing:.18em}
.top{box-shadow:inset 0 3px 0 var(--seam)}
.top-brand b{font-family:var(--poster);font-weight:900;font-stretch:76%;font-size:13px;letter-spacing:.06em}

/* The bottom bar. A white block slides between destinations instead of
   lighting up in place: the indicator is one object that moves, the way a
   segmented control moves. --i is the index of the current tab, set in app.js;
   --n the number of tabs. On a laptop the bar is a row of tabs under a rule,
   and the block becomes an underline that slides the same way. */
.bottomnav{--n:4;--i:0;position:fixed}
.bottomnav.nav-2{--n:2}.bottomnav.nav-3{--n:3}
.bottomnav::before{content:"";position:absolute;top:0;bottom:env(safe-area-inset-bottom);left:0;
  width:calc(100% / var(--n));background:#fff;
  transform:translateX(calc(var(--i) * 100%));
  transition:transform var(--t3) var(--pop)}
.bottomnav button{position:relative;z-index:1;border-top:0;font-family:var(--meter);font-weight:400;
  font-size:11px;letter-spacing:.14em;padding:19px 4px 18px;min-height:58px;
  transition:color var(--t2) var(--ease)}
.bottomnav button::after{content:none}
.bottomnav button[aria-current="page"]{background:transparent;color:var(--navy)}
@media(min-width:900px){
  .bottomnav{position:relative;--w:0px}
  /* the coach's roster runs wide; its tab row lines up with it, not with the family app's column */
  body:has(#main.wrap-wide) .bottomnav{max-width:1240px}
  .bottomnav::before{top:auto;bottom:-2px;height:3px;width:var(--w);left:var(--x,0px);
    background:var(--navy);transform:none;
    transition:left var(--t3) var(--pop),width var(--t3) var(--ease)}
  .bottomnav button{border-bottom:0;padding:22px 24px 19px}
}
/* On a phone the date sits under the word; the DOM is word first now, so the
   phone order flips back with the flex order it always had visually. */
@media(max-width:400px){.brief-head .brief-date{order:-1}}

/* ---- buttons ------------------------------------------------------------ */
.btn{font-family:var(--poster);font-weight:900;font-stretch:88%;font-size:13.5px;letter-spacing:.12em}
.btn-sm{font-size:11.5px}
.btn-navy{box-shadow:inset 0 0 0 0 var(--seam)}
/* the press: a hair of scale and the lit edge crossing, on every primary */
@media (prefers-reduced-motion: no-preference){
  .btn-navy::after{content:"";position:absolute;top:-60%;bottom:-60%;width:36%;
    background:rgba(255,255,255,.16);transform:translateX(-320%) skewX(-18deg);pointer-events:none}
  .btn-navy:active::after{animation:sheen var(--t3) var(--ease) 1 both}
}
.backlink{font-family:var(--meter);font-weight:400;letter-spacing:.16em;font-size:11px}

/* ---- the tick ----------------------------------------------------------- */
.items .n.tick{-webkit-text-stroke:0;color:var(--index);font-size:15px;font-family:var(--meter);
  border:1.5px solid var(--line)}
.items li.ticked .n.tick{border-color:var(--navy);font-family:var(--sans);font-weight:700}
@media (prefers-reduced-motion: no-preference){
  .items li.ticked .n.tick{animation:tickpop var(--t3) var(--pop) both}
  @keyframes tickpop{0%{transform:scale(.6)}60%{transform:scale(1.12)}100%{transform:scale(1)}}
  .items li.ticked b{transition:color var(--t2) var(--ease)}
}

/* ---- roster and rows ---------------------------------------------------- */
.roster-row{padding:22px 0 24px}
.roster-name{font-size:clamp(24px,6vw,34px);line-height:.9}
.needs{border-left-color:var(--seam)}
.needs .label{color:var(--seam)}
.sec{border-top-width:3px}
.sec-sub{border-top-width:1px}
.cal-day.today .d{position:relative}
.cal-day.today .d::after{content:"";position:absolute;right:-2px;top:-2px;width:6px;height:6px;
  background:var(--seam)}
.week-day.today{box-shadow:inset 0 3px 0 0 var(--seam)}

/* ---- the cascade -------------------------------------------------------- */
/* A screen arrives as a whole, then each block of it settles into place a
   beat after the one above. Blur on the way in is what makes it read as
   focus pulling rather than a fade. */
@media (prefers-reduced-motion: no-preference){
  .view-fwd{animation:vfwd var(--t3) var(--ease) both}
  @keyframes vfwd{from{opacity:0;transform:translateX(34px) scale(.985);filter:blur(6px)}
                  to{opacity:1;transform:none;filter:blur(0)}}
  .view-back{animation:vback var(--t3) var(--ease) both}
  @keyframes vback{from{opacity:0;transform:translateX(-26px) scale(.985);filter:blur(6px)}
                   to{opacity:1;transform:none;filter:blur(0)}}
  .view-tab,.view-in{animation:vtab var(--t3) var(--ease) both}
  @keyframes vtab{from{opacity:0;transform:translateY(14px);filter:blur(4px)}
                  to{opacity:1;transform:none;filter:blur(0)}}
  .view-fwd > *,.view-back > *,.view-tab > *,.view-in > *{
    animation:rise 620ms var(--ease) both;animation-delay:calc(var(--k,0) * 45ms + 40ms)}
  @keyframes rise{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
  .view-fwd > :nth-child(1),.view-back > :nth-child(1),.view-tab > :nth-child(1),.view-in > :nth-child(1){--k:0}
  .view-fwd > :nth-child(2),.view-back > :nth-child(2),.view-tab > :nth-child(2),.view-in > :nth-child(2){--k:1}
  .view-fwd > :nth-child(3),.view-back > :nth-child(3),.view-tab > :nth-child(3),.view-in > :nth-child(3){--k:2}
  .view-fwd > :nth-child(4),.view-back > :nth-child(4),.view-tab > :nth-child(4),.view-in > :nth-child(4){--k:3}
  .view-fwd > :nth-child(5),.view-back > :nth-child(5),.view-tab > :nth-child(5),.view-in > :nth-child(5){--k:4}
  .view-fwd > :nth-child(6),.view-back > :nth-child(6),.view-tab > :nth-child(6),.view-in > :nth-child(6){--k:5}
  .view-fwd > :nth-child(n+7),.view-back > :nth-child(n+7),.view-tab > :nth-child(n+7),.view-in > :nth-child(n+7){--k:6}
  /* a title is cut in from below, the way the hero title arrives on the site */
  .view-fwd h1,.view-back h1,.view-tab h1,.view-in h1,.view-tab .brief-word,.view-in .brief-word{
    animation:cutin 760ms var(--ease) both;animation-delay:60ms}
  @keyframes cutin{from{clip-path:inset(0 0 100% 0);transform:translateY(24px)}
                   to{clip-path:inset(-10% 0 -10% 0);transform:none}}
  .view-fwd h1::after,.view-back h1::after,.view-tab h1::after,.view-in h1::after{
    animation:seamdraw 520ms var(--ease) both;animation-delay:560ms}
  @keyframes seamdraw{from{transform:scaleX(0)}to{transform:scaleX(1)}}
  /* the slab title arrives heavier: cut in, then the depth pushes out */
  .view-tab .today-hero h1,.view-in .today-hero h1,.view-tab .today-hero .slab-title,.view-in .today-hero .slab-title{
    animation:cutin 820ms var(--ease) both,depth 900ms var(--ease) 500ms both;animation-delay:120ms,500ms}
  @keyframes depth{from{text-shadow:0 0 0 var(--navy-mid),0 0 0 var(--navy-mid),0 0 0 var(--navy-mid),0 0 0 var(--navy-mid)}
                   to{text-shadow:1px 1px 0 var(--navy-mid),2px 2px 0 var(--navy-mid),3px 3px 0 var(--navy-mid),4px 4px 0 var(--navy-mid)}}
  /* a number that appears counts itself in, quickly */
  .view-tab .streak-n,.view-in .streak-n,.view-tab .cal-tally b,.view-in .cal-tally b{
    animation:rise 700ms var(--pop) both;animation-delay:220ms}
}

/* The seam-red status dot on the bar when a screen has condensed: the title
   arrives from below and the brand leaves upward, both a touch slower now. */
.appbar-title,.appbar .brand{transition-duration:var(--t3)}

/* ============================================================================
   THE NATIVE CUT  (25 Sep 2026)
   The launch, the flight of a title, sheets that come up from the thumb, and
   the small physics that make glass feel like a device instead of a page.
   ============================================================================ */

/* ---- the launch --------------------------------------------------------- */
.launch{position:fixed;inset:0;z-index:200;background:var(--navy);color:#fff;
  display:grid;place-items:center;overflow:hidden;
  padding-top:env(safe-area-inset-top);padding-bottom:env(safe-area-inset-bottom)}
.launch::before{content:"";position:absolute;inset:0;
  background:radial-gradient(90% 60% at 50% 40%,rgba(255,255,255,.08),transparent 70%)}
.launch-mark{position:relative;display:grid;justify-items:center;gap:18px;text-align:center}
.launch-mark img{width:72px;height:72px;border-radius:50%;background:#fff;
  animation:lfocus .9s var(--ease) .05s both}
.launch-word{font-family:var(--poster);font-weight:900;font-stretch:76%;text-transform:uppercase;
  font-size:15px;letter-spacing:.08em;animation:ltrack .9s var(--ease) .3s both}
.launch-meter{font-family:var(--meter);font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;
  color:rgba(255,255,255,.55);animation:lfocus .7s var(--ease) .55s both}
.launch-seam{position:absolute;left:0;right:0;top:calc(50% + 96px);height:2px;background:var(--seam);
  transform:scaleX(0);transform-origin:left;animation:lseam .6s var(--ease) .5s both}
@keyframes lfocus{from{opacity:0;transform:scale(.86);filter:blur(10px)}to{opacity:1;transform:none;filter:blur(0)}}
@keyframes ltrack{from{opacity:0;letter-spacing:.5em;transform:translateY(6px)}to{opacity:1;letter-spacing:.08em;transform:none}}
@keyframes lseam{from{transform:scaleX(0)}to{transform:scaleX(1)}}
/* and off: the field lifts from the bottom edge, the content already there */
.launch.lift{animation:llift .62s cubic-bezier(.7,0,.2,1) both;pointer-events:none}
.launch.lift .launch-mark,.launch.lift .launch-seam{animation:lgone .25s var(--ease) both}
@keyframes llift{to{clip-path:inset(0 0 100% 0)}}
@keyframes lgone{to{opacity:0;transform:translateY(-10px)}}
/* while the field is up, the screen under it holds its entrance until the lift */
html.launching #main > *{animation-play-state:paused!important}
html.launching #main h1,html.launching #main .brief-word{animation-play-state:paused!important}
@media (prefers-reduced-motion: reduce){.launch{display:none}}

/* ---- the flight --------------------------------------------------------- */
.fly{position:fixed;z-index:150;pointer-events:none;margin:0;
  font-family:var(--poster);font-weight:900;font-stretch:74%;font-variation-settings:"wdth" 74;
  text-transform:uppercase;letter-spacing:-.02em;line-height:.82;color:var(--navy);
  transform-origin:top left;white-space:nowrap;will-change:transform}
.roster-row b{transform-origin:left center}

/* ---- sheets ------------------------------------------------------------- */
dialog::backdrop{background:rgba(4,23,47,.5);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
@media (max-width: 639px){
  dialog{border-top:0;box-shadow:inset 0 3px 0 var(--seam),0 -20px 60px rgba(4,23,47,.25)}
  dialog .dlg-in::before{width:44px;height:4px;background:var(--line);margin:0 auto 20px}
  dialog .dlg-in{padding-top:14px}
}
@media (max-width: 639px) and (prefers-reduced-motion: no-preference){
  dialog[open]{animation:sheetup var(--t3) cubic-bezier(.22,1,.36,1) both}
  @keyframes sheetup{from{transform:translateY(100%)}to{transform:none}}
  dialog[open]::backdrop{animation:fade var(--t2) var(--ease) both}
  dialog[open] .dlg-in > *{animation:rise 520ms var(--ease) both;animation-delay:calc(var(--k,1) * 40ms + 120ms)}
  dialog[open] .dlg-in > :nth-child(1){--k:0}dialog[open] .dlg-in > :nth-child(2){--k:1}
  dialog[open] .dlg-in > :nth-child(3){--k:2}dialog[open] .dlg-in > :nth-child(4){--k:3}
  dialog[open] .dlg-in > :nth-child(n+5){--k:4}
}
.dlg-in h2{font-family:var(--poster);font-weight:900;font-stretch:74%;font-size:clamp(30px,8vw,40px);
  letter-spacing:-.02em;line-height:.86}
.dlg-in .hint{margin-top:6px}

/* ---- device physics ----------------------------------------------------- */
*{-webkit-tap-highlight-color:transparent}
button,a,input,select,textarea,label{touch-action:manipulation}
html{scroll-padding-top:70px}
.page{padding-bottom:calc(112px + env(safe-area-inset-bottom))}
@media(min-width:900px){.page{padding-bottom:64px}}
/* a sticky bar's shadow says it is above the content only once content is under it */
.appbar{transition:box-shadow var(--t2) var(--ease)}
.appbar.condensed{box-shadow:inset 0 3px 0 var(--seam),0 1px 0 rgba(255,255,255,.08),0 8px 24px rgba(4,23,47,.18)}
/* the row you press answers before it opens */
.roster-row:active b{transform:scale(.985)}
.roster-row b{transition:transform var(--t1) var(--ease)}
/* inputs: a hard baseline that thickens on focus, no box, no glow */
input:not([type=checkbox]):not([type=radio]):not([type=range]),select,textarea{
  transition:border-color var(--t1) var(--ease),box-shadow var(--t1) var(--ease)}
input:focus-visible,select:focus-visible,textarea:focus-visible{outline:0;
  border-color:var(--navy);box-shadow:0 2px 0 0 var(--navy)}

/* ---- empty states ------------------------------------------------------- */
.empty{padding:48px 0 44px;max-width:none;position:relative}
.empty::before{content:"";display:block;width:44px;height:3px;background:var(--seam);margin-bottom:26px}
.empty b{font-family:var(--poster);font-weight:900;font-stretch:74%;font-variation-settings:"wdth" 74;
  text-transform:uppercase;letter-spacing:-.02em;line-height:.86;font-size:clamp(34px,9vw,54px);
  color:var(--navy);margin-bottom:18px}
.empty p{max-width:34ch}
.empty .btn{margin-top:26px}

/* ---- saved, done, error: feedback in place ------------------------------ */
.saved{font-family:var(--meter);font-weight:400;letter-spacing:.2em}
.saved::before{content:"";display:inline-block;width:14px;height:1px;background:var(--ok);margin-right:8px}
.savebar{box-shadow:0 -8px 24px rgba(4,23,47,.08)}

/* Today, reordered 27 Sep 2026: the work is the first thing on the screen and
   Jack's note and tip follow it. The briefing block therefore opens with a
   rule instead of closing with one. */
.brief-after{border-bottom:0;border-top:3px solid var(--rule);padding:26px 0 8px;margin-top:0}
.brief-after > .label{margin-bottom:6px}
.today-hero .kicker{flex-wrap:wrap;row-gap:6px}
.setup-card{margin-top:22px}

/* ---- the demo player ---------------------------------------------------- */
/* A sheet that is mostly the picture. One line of who and what above it, the
   clip edge to edge, one quiet way out to the app below it. One close control,
   top right, where a thumb on a phone and a pointer on a laptop both expect it. */
dialog.player{max-width:760px;padding:0}
.player-in{padding:12px var(--pad) calc(18px + env(safe-area-inset-bottom))}
.player-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin:2px 0 16px}
.player-titles{min-width:0}
.player-titles .label{margin-bottom:8px}
.player-name{display:block;font-family:var(--poster);font-weight:900;font-stretch:74%;font-variation-settings:"wdth" 74;
  text-transform:uppercase;font-size:clamp(26px,7vw,36px);line-height:.88;letter-spacing:-.02em;color:var(--navy);
  overflow-wrap:anywhere}
.player-presc{display:block;margin-top:8px;font-family:var(--meter);font-size:11px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--quiet)}
.player-x{flex:none;width:44px;height:44px;margin:-6px -10px 0 0;border:0;background:none;color:var(--navy);
  font-size:32px;line-height:1;cursor:pointer;display:grid;place-items:center;transition:opacity var(--t1) var(--ease)}
.player-x:active{opacity:.5}
.player-frame{position:relative;aspect-ratio:16/9;background:var(--navy-deep);
  margin:0 calc(var(--pad) * -1);overflow:hidden}
.player-frame::before{content:"";position:absolute;left:50%;top:50%;width:64px;height:64px;margin:-32px 0 0 -32px;
  border-radius:50%;border:2px solid rgba(255,255,255,.35);
  background:linear-gradient(90deg,transparent 0 40%,rgba(255,255,255,.9) 40% 100%) no-repeat;
  background-size:0 0;transition:opacity var(--t2) var(--ease)}
.player-frame::after{content:"";position:absolute;left:50%;top:50%;width:0;height:0;margin:-11px 0 0 -7px;
  border-style:solid;border-width:11px 0 11px 19px;border-color:transparent transparent transparent rgba(255,255,255,.9);
  transition:opacity var(--t2) var(--ease)}
.player-frame.is-ready::before,.player-frame.is-ready::after{opacity:0}
.player-frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;background:#000}
.player-foot{display:flex;justify-content:flex-end;margin-top:14px}
.player-out{display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:0 2px;text-decoration:none;
  font-family:var(--meter);font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--quiet);
  border-bottom:1px solid var(--line);transition:color var(--t1) var(--ease),border-color var(--t1) var(--ease)}
.player-out:hover{color:var(--navy);border-color:var(--navy)}
.player-out .arw{font-size:13px}
@media(min-width:640px){
  .player-frame{margin:0}
  .player-in{padding:18px 26px 22px}
}
@media (max-width:639px){
  dialog.player{max-height:none;box-shadow:inset 0 3px 0 var(--seam),0 -20px 60px rgba(4,23,47,.25)}
  dialog.player .dlg-in::before{margin-bottom:14px}
}
