/* ==========================================================================
   Factbox — sign up, onboarding, plans.

   Loaded AFTER css/app.css and adds only what the funnel needs that the
   library did not: a step indicator, a text field, choice buttons and the
   three plan cards. The page frame, the nav tabs, the primary button, the
   fine print and every colour token come from app.css unchanged. A funnel
   that looks like a different site is a funnel people abandon.

   Same commitments as the rest of the site: the ground and the type colour
   painted explicitly rather than inherited, every colour an app.css token,
   44px targets, and nothing tappable below --bottom-safe, where
   the Instagram / TikTok toolbar sits. Everything here is prefixed .jn- so it
   cannot collide with a class another layer owns.
   ========================================================================== */

/* Painted rather than inherited, so a load-order change cannot leave this
   page transparent over whatever the host webview paints. */
body{background:var(--ground); color:var(--parchment)}

/* Visually hidden, but read aloud. */
.jn-vh{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

/* --------------------------------------------------------------------------
   Step indicator. Four segments and a sentence — "Step 2 of 4" — because a
   bar with no number tells you there is more without telling you how much.
   -------------------------------------------------------------------------- */
.jn-steps{
  display:flex; gap:6px; margin:16px 2px 8px; padding:0; list-style:none;
}
.jn-steps li{
  flex:1 1 0; height:4px; border-radius:2px;
  background:var(--tint-3);
  transition:background .2s ease;
}
.jn-steps li.is-done{background:var(--coral-deep)}
.jn-steps li.is-on{background:var(--coral)}
.jn-stepnote{
  margin:0 2px 2px; font-size:var(--fs-meta); font-weight:var(--w-strong);
  letter-spacing:0; color:var(--dimmer); font-variant-numeric:tabular-nums;
}

/* --------------------------------------------------------------------------
   Panels. One screen per step; the rest are [hidden], which is an attribute
   the browser honours with no CSS at all — so a stylesheet that fails to load
   shows every step stacked and readable rather than nothing.
   -------------------------------------------------------------------------- */
.jn-panel{margin-top:18px}
.jn-panel[hidden]{display:none}
.jn-panel h2{
  font-size:clamp(1.45rem,5.8vw,1.85rem); font-weight:var(--w-head); margin:0 0 8px;
  line-height:var(--lh-head); letter-spacing:var(--ls-head); text-wrap:balance;
}
.jn-panel > p{
  margin:0 0 4px; color:var(--dim); font-size:var(--fs-body); line-height:var(--lh-body);
  max-width:44ch;
}

/* --------------------------------------------------------------------------
   The email field. One input and an optional first name, asked late — the
   funnel earns the answer before it asks for the address, which is the whole
   point of putting the questions first.
   -------------------------------------------------------------------------- */
.jn-field{display:block; margin:16px 0 0}
.jn-field > span{
  display:block; margin:0 0 7px 2px;
  font-size:var(--fs-note); font-weight:var(--w-strong); color:var(--dim);
}
.jn-field input{
  width:100%; min-height:var(--tap);
  padding:14px 16px; border:0; border-radius:14px;
  background:var(--raise); color:var(--parchment);
  font-family:var(--ui); font-size:var(--fs-body); font-weight:var(--w-label);
  box-shadow:inset 0 0 0 1px var(--hair);
  -webkit-appearance:none; appearance:none;
}
/* A placeholder is still text a reader has to parse, so it gets --dimmer's
   4.6:1 rather than the .38 wash it carried, which measured 1.9:1 on paper. */
.jn-field input::placeholder{color:var(--dimmer)}
/* A REAL focus indicator on the two fields a keyboard user needs it on most.

   This rule used to be `box-shadow:inset 0 0 0 1px var(--coral); outline:none`
   — it removed the site's ring and replaced it with a 1px inset hairline in a
   colour app.css calls a FILL, never type. On the sign-in page that is the one
   place where "where am I?" has to be answerable without a mouse, and it was
   not: a 1px inset line inside a filled field, at 2.0:1.

   The outline is back, in --ring, and the inset line stays as the field's own
   "this one is live" fill. :focus, not :focus-visible, because a text field
   shows its ring on a tap as well: the caret is inside it either way. */
.jn-field input:focus{
  box-shadow:inset 0 0 0 1px var(--accent-edge);
  outline:2px solid var(--ring); outline-offset:2px;
}
.jn-opt-note{
  margin:7px 2px 0; font-size:var(--fs-meta); font-weight:var(--w-label);
  color:var(--dimmer); line-height:1.45;
}

/* An error is a sentence, in the one colour that means "look here", and it is
   never the only signal: the field keeps focus and the button stays live.

   --crimson, not --accent-ink. In link-blue an error read as one more piece of
   the page's own furniture — the same colour as every link and eyebrow on it.
   4.94:1 on --ground, 5.90:1 on --raise.

   The selector carries .au-panel because css/auth.css loads after this file
   and its `.au-panel > p` is (0,2,0) against this rule's (0,1,0): on
   /login every one of these five declarations lost, and the error rendered as
   16px/400 body copy in --dim. Same specificity defect as .cite. */
.jn-err,
.au-panel > p.jn-err{
  margin:10px 2px 0; font-size:var(--fs-note); font-weight:var(--w-strong);
  line-height:1.45; color:var(--crimson);
}
.jn-err[hidden]{display:none}

/* --------------------------------------------------------------------------
   Choice buttons — what draws you, what sounds familiar, how much time, how
   long a streak. Big enough to hit, wrapped so a 390px screen never scrolls
   sideways, and the chosen state is a filled outline rather than a colour
   swap, so it survives a forced-colours mode.
   -------------------------------------------------------------------------- */
.jn-opts{
  display:grid; gap:9px; margin:18px 0 0;
  grid-template-columns:repeat(auto-fill,minmax(150px,1fr));
}
.jn-opts.is-single{grid-template-columns:1fr}
.jn-opt{
  display:block; width:100%; text-align:left; cursor:pointer;
  min-height:var(--tap); padding:12px 14px;
  border:0; border-radius:14px;
  background:var(--raise); color:var(--parchment);
  font-family:var(--ui); font-size:var(--fs-sub); font-weight:var(--w-strong);
  box-shadow:inset 0 0 0 1px var(--hair);
  transition:box-shadow .15s ease, background .15s ease, transform .15s ease;
}
.jn-opt:active{transform:scale(.985)}
.jn-opt b{
  display:block; font-size:var(--fs-sub); font-weight:var(--w-head);
  line-height:var(--lh-tight);
}
.jn-opt span{
  display:block; margin-top:3px;
  font-size:var(--fs-meta); font-weight:var(--w-label);
  color:var(--dimmer); line-height:1.35;
}
.jn-opt[aria-pressed="true"],
.jn-opt[aria-checked="true"]{
  background:var(--accent-wash);
  box-shadow:inset 0 0 0 2px var(--coral);
}
.jn-opt[aria-pressed="true"] span,
.jn-opt[aria-checked="true"] span{color:var(--dim)}

/* --------------------------------------------------------------------------
   Plans. Three cards, one screen. Every card carries BOTH numbers — the per
   month figure and the amount actually charged — because a per-month price on
   a plan billed in a lump is half the truth, and the half that gets a refund
   request.
   -------------------------------------------------------------------------- */
.jn-plans{display:grid; gap:11px; margin:18px 0 0}
.jn-plan{
  position:relative; display:block; width:100%; text-align:left; cursor:pointer;
  min-height:var(--tap); padding:15px 16px;
  border:0; border-radius:16px;
  background:var(--raise); color:var(--parchment);
  font-family:var(--ui);
  box-shadow:inset 0 0 0 1px var(--hair);
  transition:box-shadow .15s ease, background .15s ease, transform .15s ease;
}
.jn-plan:active{transform:scale(.99)}
.jn-plan[aria-checked="true"]{
  background:var(--accent-wash);
  box-shadow:inset 0 0 0 2px var(--coral);
}
.jn-plan .jn-per{
  display:block; font-size:var(--fs-h3); font-weight:var(--w-head);
  letter-spacing:var(--ls-hero); line-height:1.1;
}
.jn-plan .jn-per i{
  font-style:normal; font-size:var(--fs-note); font-weight:var(--w-strong);
  color:var(--dim);
}
.jn-plan .jn-billed{
  display:block; margin-top:5px;
  font-size:var(--fs-note); font-weight:var(--w-label); color:var(--dim);
  line-height:1.4;
}
.jn-plan .jn-save{
  display:inline-block; margin-top:8px;
  padding:3px 9px; border-radius:var(--pill);
  background:var(--tint); color:var(--parchment);
  font-size:var(--fs-micro); font-weight:var(--w-head); letter-spacing:var(--ls-badge);
}
/* The best-value marker is the one piece of emphasis on this screen, and it
   is a statement of arithmetic — the cheapest month — not a scarcity claim. */
.jn-plan.is-best{box-shadow:inset 0 0 0 1px var(--accent-edge)}
.jn-plan.is-best[aria-checked="true"]{box-shadow:inset 0 0 0 2px var(--coral)}
.jn-badge{
  position:absolute; right:14px; top:-9px;
  padding:3px 9px; border-radius:var(--pill);
  background:var(--coral); color:var(--on-accent);
  font-size:var(--fs-micro); font-weight:var(--w-head); letter-spacing:var(--ls-badge);
}

/* The trial sentence sits directly under the button it describes. */
.jn-terms{
  margin:12px auto 0; max-width:520px;
  font-size:var(--fs-meta); line-height:var(--lh-body); color:var(--dim);
}
.jn-terms b{color:var(--parchment); font-weight:var(--w-head)}

/* --------------------------------------------------------------------------
   Row of secondary actions: Back on the left, Skip on the right. Both are
   real 44px targets — a 12px "skip" is a dark pattern with extra steps.
   -------------------------------------------------------------------------- */
.jn-row{
  display:flex; align-items:center; justify-content:space-between;
  gap:12px; margin-top:14px;
}
.jn-row .ghost{margin-top:0}
.jn-back{
  display:inline-flex; align-items:center; gap:6px;
  min-height:var(--tap); padding:10px 4px;
  background:none; border:0; cursor:pointer;
  font-family:var(--ui); font-size:var(--fs-sub); font-weight:var(--w-strong);
  color:var(--dim);
}
.jn-skip{
  display:inline-flex; align-items:center;
  min-height:var(--tap); padding:10px 4px;
  background:none; border:0; cursor:pointer;
  font-family:var(--ui); font-size:var(--fs-sub); font-weight:var(--w-strong);
  color:var(--dimmer); text-decoration:underline; text-underline-offset:3px;
}

/* A boxed aside for the two things a reader has to act on themselves: moving
   access to a new phone, and a browser that is not keeping their answers.
   Same shell as .keep on the home shelf, so it reads as the same kind of
   message rather than as an error. */
.jn-aside{
  margin:18px 0 0; padding:14px 15px;
  border-radius:16px; background:var(--raise);
  box-shadow:inset 0 0 0 1px var(--hair);
}
.jn-aside[hidden]{display:none}
.jn-aside b{
  display:block; font-size:var(--fs-sub); font-weight:var(--w-head); margin-bottom:5px;
}
.jn-aside p{
  margin:0 0 8px; font-size:var(--fs-note); line-height:var(--lh-body); color:var(--dim);
}
.jn-aside p:last-child{margin-bottom:0}
.jn-aside a{color:var(--accent-ink); font-weight:var(--w-strong)}
.jn-aside input{
  width:100%; margin:2px 0 9px; padding:11px 12px;
  border:0; border-radius:10px; background:var(--tint);
  color:var(--parchment); font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:var(--fs-meta); min-height:var(--tap);
}

@media (prefers-reduced-motion:reduce){
  .jn-steps li, .jn-opt, .jn-plan{transition:none}
}

/* "First name optional" — the qualifier reads as a qualifier, not as part of
   the label. */
.jn-field > span i{font-style:normal; font-weight:var(--w-label); color:var(--dimmer)}

/* ==========================================================================
   THE PORTED FUNNEL
   Everything below serves the iOS onboarding's shape, brought to the web:
   ask something easy, echo it back warmly, build to a commitment, then
   assemble a plan in front of the reader. Colours, radii, tap size and type
   all come from app.css — this adds arrangement, not a second palette.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Reinforcement. The one line that says the last answer was heard, and what
   it bought. It appears one step AFTER the question it echoes, which is the
   whole trick — the reader sees their own answer paying off on a screen they
   did not expect it on.
   -------------------------------------------------------------------------- */
.jn-reinf{
  margin:16px 0 0; padding:12px 14px;
  border-radius:14px; background:var(--accent-wash);
  box-shadow:inset 0 0 0 1px var(--accent-edge);
  font-size:var(--fs-sub); font-weight:var(--w-strong); line-height:1.45;
  color:var(--parchment);
}
.jn-reinf[hidden]{display:none}

/* --------------------------------------------------------------------------
   The echo screen. A whole screen that asks nothing — it repeats the answer
   back, then names the thing that answer just bought. In the iOS flow this
   is the step people remember, so it gets the biggest type on the funnel and
   no competing controls.
   -------------------------------------------------------------------------- */
.jn-echo{
  margin:20px 0 0; padding:24px 20px;
  border-radius:22px; background:var(--accent-wash);
  box-shadow:inset 0 0 0 1px var(--accent-edge);
  text-align:center;
}
.jn-echo .jn-eyebrow{
  margin:0 0 12px; font-size:var(--fs-micro); font-weight:var(--w-label);
  letter-spacing:var(--ls-caps); color:var(--accent-ink); text-transform:uppercase;
}
.jn-echo h2{
  font-size:clamp(1.5rem,6.2vw,2rem); font-weight:var(--w-head); margin:0 0 12px;
  line-height:var(--lh-head); letter-spacing:var(--ls-head); text-wrap:balance;
}
.jn-echo p{
  margin:0 0 12px; max-width:34ch; margin-left:auto; margin-right:auto;
  font-size:var(--fs-body); line-height:var(--lh-body); color:var(--dim);
}
/* The benefit, not the compliment, is the sentence that converts. */
.jn-echo p.jn-benefit{
  margin-bottom:0; color:var(--parchment); font-weight:var(--w-strong);
}

/* --------------------------------------------------------------------------
   The "sound familiar?" replies. The statement is always readable; the small
   nod under it is a REPLY to a tap, so it arrives with the tap. Nothing that
   is content hides here — only the response to an action not yet taken.
   -------------------------------------------------------------------------- */
.jn-opt .reply{display:none}
.jn-opt[aria-pressed="true"] .reply{display:block; color:var(--accent-ink)}

/* --------------------------------------------------------------------------
   Streak rows. A day count in a badge, then the adjective the reader is
   buying. The numeral is the point, so it is the biggest thing in the row.
   -------------------------------------------------------------------------- */
.jn-opt.is-streak{display:flex; align-items:center; gap:12px}
.jn-opt .jn-t{
  display:block; margin:0; min-width:0;
  font-size:inherit; font-weight:inherit; color:inherit;
}
.jn-day{
  flex:0 0 46px; height:46px; border-radius:12px;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  background:var(--tint); color:var(--parchment);
  font-style:normal; font-size:var(--fs-lead); font-weight:var(--w-head); line-height:1;
  font-variant-numeric:tabular-nums;
  transition:background .15s ease, color .15s ease;
}
.jn-day u{
  text-decoration:none; margin-top:3px;
  font-size:.6rem; font-weight:var(--w-head); letter-spacing:var(--ls-badge);
  text-transform:uppercase; color:var(--dimmer);
}
.jn-opt[aria-checked="true"] .jn-day{background:var(--accent-wash); color:var(--accent-ink)}
.jn-opt[aria-checked="true"] .jn-day u{color:var(--accent-ink)}

/* --------------------------------------------------------------------------
   The plan loader. Three bars that fill, stall, ask one thing, then finish.

   It is theatre and the code says so — but it is the beat that converts,
   because it shows work being done on the reader's behalf using the answers
   they just gave. The bars are `%` widths set from script; if the script
   never runs, the markup already has them full and the phases read as a
   plain list of what the plan is made of.
   -------------------------------------------------------------------------- */
.jn-phases{
  list-style:none; margin:18px 0 0; padding:15px 16px;
  border-radius:18px; background:var(--raise);
  box-shadow:inset 0 0 0 1px var(--hair);
}
.jn-phase + .jn-phase{margin-top:16px}
.jn-phase.is-waiting{opacity:.45}
.jn-phase-top{
  display:flex; align-items:baseline; justify-content:space-between; gap:10px;
  margin:0 0 7px;
}
.jn-phase-top b{font-size:var(--fs-sub); font-weight:var(--w-head); line-height:1.3}
.jn-phase-top i{
  flex:none; font-style:normal;
  font-size:var(--fs-meta); font-weight:var(--w-strong); color:var(--dimmer);
  font-variant-numeric:tabular-nums;
}
/* --teal is the sage FILL, which is 1.9:1 as type on cream. The bar beside
   this number stays sage; the number itself takes the ink version. */
.jn-phase.is-done .jn-phase-top i{color:var(--teal-ink)}
.jn-bar{
  height:6px; border-radius:3px; overflow:hidden;
  background:var(--tint-3);
}
.jn-bar > i{
  display:block; height:100%; width:100%;
  background:var(--coral); border-radius:3px;
  transition:width .18s linear;
}
.jn-phase.is-done .jn-bar > i{background:var(--teal)}

/* The question that floats up out of the stalled bar. Tethered to it, so it
   reads as the reason the bar stopped rather than as a new screen. */
.jn-q{
  margin:12px 0 2px; padding:14px 15px;
  border-radius:14px; background:var(--tint);
  box-shadow:inset 0 0 0 1px var(--hair);
}
.jn-q[hidden]{display:none}
.jn-q .jn-q-eyebrow{
  margin:0 0 6px; font-size:var(--fs-micro); font-weight:var(--w-label);
  letter-spacing:var(--ls-caps); color:var(--accent-ink); text-transform:uppercase;
}
.jn-q .jn-q-t{
  margin:0 0 12px; font-size:var(--fs-body); font-weight:var(--w-head);
  line-height:1.35; color:var(--parchment);
}
.jn-q-row{display:grid; grid-template-columns:1fr 1fr; gap:10px}
.jn-yn{
  min-height:var(--tap); padding:12px 10px;
  border:0; border-radius:12px; cursor:pointer;
  background:var(--tint); color:var(--parchment);
  font-family:var(--ui); font-size:var(--fs-sub); font-weight:var(--w-head);
  box-shadow:inset 0 0 0 1px var(--hair);
  transition:transform .15s ease, background .15s ease;
}
.jn-yn:active{transform:scale(.985)}
/* Sage, written out rather than mixed from --teal. color-mix() is Safari 16.2
   and Chrome 111; an in-app webview older than that drops the declaration as
   invalid, and the "yes" state would silently stop showing at all. This page
   is only ever served on the paper ground, so the literal is the whole truth. */
.jn-yn.is-yes{
  background:rgba(140,167,93,.30);
  box-shadow:inset 0 0 0 1px rgba(71,98,32,.55);
}

@media (prefers-reduced-motion:reduce){
  .jn-bar > i, .jn-day, .jn-yn{transition:none}
}

/* A way back in from anywhere in the funnel. Someone returning on a new phone
   is not "signing up" — and without this their only visible route through is
   to pay a second time. */
.jn-haveacct{
  margin:14px 0 0; text-align:center; font-size:var(--fs-note);
  color:var(--dimmer);
}
.jn-haveacct a{
  color:var(--accent-ink); font-weight:var(--w-strong); text-decoration:none;
  display:inline-block; padding:9px 6px; min-height:var(--tap); line-height:26px;
}

/* ==========================================================================
   THE ACCOUNT SCREEN AND ITS SETTINGS — /account and /settings

   Everything below is prefixed .ac- so it cannot collide with .jn- (the
   funnel, above), .au- (css/auth.css, which loads AFTER this file) or
   anything the core owns. Colours are app.css tokens only; there is not one
   literal in this block.

   TWO SPECIFICITY TRAPS, BOTH ALREADY PAID FOR ON THIS SITE.

   1. css/auth.css loads after this file and carries `.au-panel > p` at
      (0,1,1). Every eyebrow, name and streak line here is a <p>, and a bare
      class is (0,1,0) — it would lose, exactly the way .au-or and .au-say
      did before their selectors were doubled up. Rather than double seven
      selectors, both pages wrap their contents in ONE .ac-body div, so no <p>
      of ours is a DIRECT child of .au-panel and the rule cannot reach them.
      That wrapper is load-bearing. Do not flatten it.

   2. `.au-panel{text-align:center}` in auth.css centres the sign-in screen.
      The mockup's account and settings screens are a left-aligned column, so
      .ac-body sets text-align:left and the two centred things on the page —
      the sign-out button and the legal line — opt back in for themselves.
   -------------------------------------------------------------------------- */

.ac-body{text-align:left}

/* The eyebrow over every section. 11px, small capitals, --dimmer: 5.1:1 on
   --ground and 5.4:1 on --raise, which is what app.css's own note measures it
   at and what an 11px label needs. */
.ac-eyebrow{
  margin:30px 2px 12px;
  font-size:var(--fs-micro); font-weight:var(--w-label);
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--dimmer);
}
/* The first eyebrow on a screen follows the header, not another section. */
.ac-body > .ac-eyebrow:first-child{margin-top:22px}

.ac-sec[hidden]{display:none}

/* --------------------------------------------------------------------------
   Who you are. The insignia at 52px, the name, and one line under it.

   The circle is the same fill and hairline as .acct-btn.is-in in app.css —
   deliberately, because it is the same object at a different size, and the
   reader should recognise the thing they tapped to get here.
   -------------------------------------------------------------------------- */
.ac-who{
  display:flex; align-items:center; gap:13px; margin:6px 2px 0;
}
.ac-avatar{
  flex:0 0 52px; width:52px; height:52px; border-radius:var(--pill);
  display:flex; align-items:center; justify-content:center;
  background:var(--accent-wash);
  box-shadow:inset 0 0 0 1px var(--accent-edge);
  color:var(--accent-ink);
  font-size:1.3125rem; font-weight:var(--w-head); line-height:1;
}
/* Signed out, or a name we do not have, gets app.css's drawn figure rather
   than an initial — the same glyph .acct-ini.is-anon draws in the masthead,
   scaled to this circle. An emoji here would be somebody else's artwork at a
   different size on every platform, sitting beside our own wordmark. */
.ac-avatar .acct-ini.is-anon{width:20px; height:20px}
.ac-avatar .acct-ini.is-anon::before{
  width:10px; height:10px; margin-left:-5px;
  box-shadow:inset 0 0 0 2px currentColor;
}
.ac-avatar .acct-ini.is-anon::after{
  width:20px; height:10px; margin-left:-10px; border-radius:11px 11px 0 0;
  box-shadow:inset 0 0 0 2px currentColor;
}
.ac-whotext{min-width:0}
.ac-name{
  margin:0; font-size:1.3125rem; font-weight:var(--w-head);
  letter-spacing:var(--ls-title); line-height:var(--lh-tight);
  color:var(--parchment); word-break:break-word;
}
.ac-member{
  margin:3px 0 0; font-size:var(--fs-meta); font-weight:var(--w-label);
  color:var(--dimmer); word-break:break-word;
}

/* --------------------------------------------------------------------------
   Your Factbox — three counts.

   Every one of them is counted from what js/progress.js actually stores, and
   the whole row is [hidden] when there is no reading record. There is no
   zero state here: three zeroes under "Your Factbox" is a worse screen than
   no row at all, and a count invented to fill the space would be a lie.
   -------------------------------------------------------------------------- */
.ac-stats{
  display:flex; align-items:stretch; margin:0 2px; padding:0; list-style:none;
}
.ac-stats li{flex:1 1 0; padding:0 4px; min-width:0}
.ac-stats b{
  display:block; font-size:1.6875rem; font-weight:var(--w-head);
  line-height:1; letter-spacing:var(--ls-hero);
  font-variant-numeric:tabular-nums; color:var(--parchment);
}
.ac-stats span{
  display:block; margin-top:5px;
  font-size:var(--fs-micro); font-weight:var(--w-label);
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--dimmer);
}

/* --------------------------------------------------------------------------
   This week. Seven columns, a letter over each, a dot under it.

   A filled dot is --coral, which app.css calls a FILL and never type — it is
   used here as exactly that, a 9px disc with no words on it. An unfilled one
   is --tint-3, the token app.css names for "an unfilled track or dot".

   The connector between two consecutive filled days is drawn as a ::before on
   the later dot, so a run of days reads as a run rather than as seven
   separate marks. It is decorative: the row is still legible with it gone.
   -------------------------------------------------------------------------- */
.ac-days{display:flex; margin:0 2px}
.ac-days span{
  flex:1 1 0; text-align:center;
  font-size:var(--fs-micro); font-weight:var(--w-label); color:var(--dimmer);
}
.ac-dots{display:flex; margin:8px 2px 0}
/* [hidden] twins: an author display: beats the UA [hidden] rule, so these
   stayed on screen with the attribute set. Confirmed by forcing .hidden
   in Chrome — both computed display:flex. */
.ac-days[hidden],
.ac-dots[hidden]{display:none !important}
.ac-dot{
  flex:1 1 0; display:flex; align-items:center; justify-content:center;
  position:relative;
}
.ac-dot i{
  display:block; width:9px; height:9px; border-radius:var(--pill);
  background:var(--tint-3);
}
.ac-dot.is-on i{background:var(--coral)}
/* Today's dot keeps a ring whether or not it is filled, so "where am I in
   this row" never depends on the fill alone. */
.ac-dot.is-today i{box-shadow:0 0 0 3px var(--accent-wash)}
.ac-dot.is-on.is-run::before{
  content:""; position:absolute; right:50%; left:-50%; top:50%;
  height:3px; margin-top:-1.5px; background:var(--coral); border-radius:2px;
}
.ac-streak{
  margin:12px 2px 0; font-size:var(--fs-note); font-weight:var(--w-strong);
  color:var(--parchment); font-variant-numeric:tabular-nums;
}

/* --------------------------------------------------------------------------
   Currently exploring. One series, its cover, how far in, and the way on.

   This is app.css's .plate inside a lifted card, the same object .tdy-serie
   is on the front page — a subject is a row, not a list of covers, and the
   plate is the story the tap actually opens.
   -------------------------------------------------------------------------- */
.ac-serie{
  display:flex; gap:14px; align-items:center;
  padding:12px; border-radius:20px;
  background:var(--raise); box-shadow:inset 0 0 0 1px var(--hair);
  text-decoration:none; color:inherit;
  transition:transform .18s ease;
}
.ac-serie:active{transform:scale(.985)}
.ac-serie .plate{width:74px; flex:0 0 74px; border-radius:14px}
.ac-serie .ac-serie-t{min-width:0; flex:1 1 auto}
.ac-serie b{
  display:block; font-size:var(--fs-lead); font-weight:var(--w-head);
  line-height:var(--lh-tight); letter-spacing:var(--ls-title);
}
.ac-serie .ac-serie-n{
  display:block; margin-top:4px;
  font-size:var(--fs-meta); font-weight:var(--w-label); color:var(--dimmer);
  font-variant-numeric:tabular-nums;
}
.ac-serie .ac-bar{
  margin-top:9px; height:6px; border-radius:var(--pill);
  background:var(--tint-3); overflow:hidden;
}
.ac-serie .ac-bar i{
  display:block; height:100%; background:var(--coral); border-radius:var(--pill);
}
.ac-serie .ac-serie-go{
  display:block; margin-top:9px;
  font-size:var(--fs-note); font-weight:var(--w-strong); color:var(--accent-ink);
}

/* --------------------------------------------------------------------------
   Your history. app.css's .card and .plate unchanged; only the track count
   and the title size are this page's, because three covers across a 390px
   phone is a 108px tile and a 15px title wraps to four lines on it.
   -------------------------------------------------------------------------- */
.ac-sechead{
  display:flex; align-items:baseline; justify-content:space-between; gap:10px;
}
.ac-sechead .ac-eyebrow{margin-bottom:0}
.ac-seeall{
  flex:none; font-size:var(--fs-note); font-weight:var(--w-strong);
  color:var(--accent-ink); text-decoration:none;
  /* A real target. The row above reads as a label; this is a link people tap
     on the move, so it gets the site's 44px rather than the height of 14px
     of text. The negative margin keeps the baseline where the design put it. */
  display:inline-flex; align-items:center; min-height:var(--tap);
  padding:0 2px; margin:calc((var(--tap) - 1.25rem) / -2) 0;
}
.ac-hist{
  display:grid; gap:12px; grid-template-columns:repeat(3,minmax(0,1fr));
  margin:12px 2px 0;
}
.ac-hist .card h3{
  font-size:var(--fs-meta);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden;
}

/* --------------------------------------------------------------------------
   A row you tap. Settings on /account; every line on /settings.

   Hairlines rather than a filled card, because on /settings there are eight
   of them in two groups and eight filled cards is a page of furniture. The
   group draws the line above each row and the last row in a group closes it,
   so a group reads as a block whichever rows the script leaves visible.
   -------------------------------------------------------------------------- */
.ac-rows{margin:8px 0 0; padding:0}
.ac-row{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  margin:0; padding:15px 2px; min-height:var(--tap);
  border-top:1px solid var(--hair);
  text-decoration:none; color:inherit;
  font-size:1rem; font-weight:var(--w-strong);
}
.ac-row[hidden]{display:none}
.ac-rows > .ac-row:last-child{border-bottom:1px solid var(--hair)}
/* The value on the right of a row, and the arrow. Both are --dimmer, which
   is 5.1:1 on --ground — this is a 15px label, not decoration. */
.ac-row dt, .ac-row > span:first-child{
  margin:0; font-size:1rem; font-weight:var(--w-strong); color:var(--parchment);
}
.ac-row dd, .ac-row > span:last-child, .ac-row .ac-arrow{
  margin:0; flex:0 1 auto; min-width:0; text-align:right;
  font-size:var(--fs-sub); font-weight:var(--w-label); color:var(--dimmer);
  word-break:break-word;
}
/* The Settings row on /account is the one row on that screen, so it carries a
   line above AND below and a little more air, exactly as the mockup draws it. */
.ac-lead{
  margin-top:32px; padding:16px 2px;
  border-top:1px solid var(--hair); border-bottom:1px solid var(--hair);
}

/* The back link on /settings. Same size and weight as a nav tab, in --dim
   (6.4:1 on --ground) rather than --dimmer, because it is the only way out of
   this screen and a way out should not be the quietest thing on it. */
.ac-back{
  display:inline-flex; align-items:center;
  min-height:var(--tap); padding:10px 2px;
  font-size:var(--fs-sub); font-weight:var(--w-strong);
  color:var(--dim); text-decoration:none;
}
.ac-back:hover{color:var(--parchment)}

/* --------------------------------------------------------------------------
   Sign out. The one destructive control on the site.

   COLOUR ONLY, and no fill. A red button competes with the blue primary
   action elsewhere on the page and turns signing out into an alarm, when the
   reader is signing out, not deleting an account — the same reasoning
   .au-2nd.is-danger carries in css/auth.css, which this replaces on the
   settings screen. --crimson on --raise measures 5.90:1 and on --ground
   4.94:1, against the 4.5:1 that 15px/600 needs; both grounds are quoted
   because this button sits on the bare page here and inside a panel there.

   The disabled state drops to --dimmer: a control that is greyed out must not
   also be shouting.
   -------------------------------------------------------------------------- */
.ac-signout-wrap{margin:34px 0 0; text-align:center}
.ac-signout{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:var(--tap); padding:10px 16px;
  background:none; border:0; cursor:pointer;
  font-family:var(--ui); font-size:var(--fs-sub); font-weight:var(--w-strong);
  color:var(--crimson);
}
.ac-signout[disabled]{color:var(--dimmer); cursor:default}
.ac-signout-note{
  margin:6px auto 0; max-width:40ch; text-align:center;
  font-size:var(--fs-fine); font-weight:var(--w-body);
  color:var(--dimmer); line-height:var(--lh-body);
}

/* The empty state. It is a real sentence with a way out of it, never a blank
   column and never a row of grey dots pretending to be a week. */
.ac-empty{
  margin:24px 0 0; padding:16px 16px;
  border-radius:16px; background:var(--raise);
  box-shadow:inset 0 0 0 1px var(--hair);
  font-size:var(--fs-note); font-weight:var(--w-label);
  color:var(--dim); line-height:var(--lh-body);
}
.ac-empty[hidden]{display:none}
.ac-empty a{color:var(--accent-ink); font-weight:var(--w-strong)}

/* --------------------------------------------------------------------------
   Both ends of the range.

   The mockup is 390x844 and everything above is written for it. .lib already
   caps the column at 900px and centres it, so at 1440 and 1920 this page is
   the same 900px column sitting in the middle of the window rather than a
   row of eight stats stretched across a desktop monitor — the two screens
   are a phone's screens and they do not become something else on a laptop.

   What DOES change with room: the history grid takes more tracks, because a
   108px cover is a phone compromise and there is no reason to keep it at
   1440. Six is the cap; beyond that the covers start to look like thumbnails
   of themselves.
   -------------------------------------------------------------------------- */
@media (min-width:560px){
  .ac-hist{grid-template-columns:repeat(4,minmax(0,1fr)); gap:14px 13px}
  .ac-hist .card h3{font-size:var(--fs-sub)}
  /* Three counts and seven dots are phone-shaped objects. Stretched across
     .lib's full 900px they stop being a row and become three numbers a long
     way apart, with a week's worth of dots reading as a timeline. 520px is
     the site's own measurement — .go, .fine and .au-2nd all stop there — so
     they sit at the head of the column rather than across the window. */
  .ac-stats, .ac-days, .ac-dots, .ac-streak{max-width:520px}
}
@media (min-width:820px){
  .ac-hist{grid-template-columns:repeat(6,minmax(0,1fr))}
}

@media (prefers-reduced-motion:reduce){
  .ac-serie{transition:none}
}

/* The legal / navigation line at the foot of both screens.

   The mockup draws it as one quiet 12px sentence with the links set in the
   same colour as the words around them. Two things are wrong with that as
   shipped code, and both are cheap to fix without changing how the line
   reads: a 16px-tall link is fine print a reader has to aim at on a moving
   bus, and a link the same colour as its sentence is only a link if you can
   see colour at all.

   So the hit areas grow to the site's --tap and the links carry an underline.
   The row still reads as fine print — the type size, the weight and the
   colour are untouched, and the negative margin keeps the line's own height
   exactly where .fine put it. This is the same trade css/auth.css's #ac-legal
   makes, in the one place on these two screens that needs it. */
.ac-fine a{
  display:inline-flex; align-items:center;
  min-height:var(--tap); padding:0 5px;
  margin:calc((var(--tap) - 1.2em) / -2) 0;
  text-decoration:underline; text-underline-offset:3px;
}
/* The negative margin above pulls the LAYOUT box back to the height of the
   text, but the 44px HIT box still hangs about 14px below the last line — and
   this paragraph is the last thing on both pages, so that overhang eats into
   the strip .lib reserves for the in-app toolbar. Measured at 932x430, the
   tightest of the eight viewports, it left 2px of clearance against a 64px
   --bottom-safe, and a real phone's safe-area inset would have taken that.
   This gives the overhang back and then the same again as cushion, written as
   the overhang's own expression so it tracks --tap rather than being a magic
   number that goes stale the day the tap size moves. Measured after: 17px of
   clearance at 932x430 and 375x667, the two tightest, and more everywhere
   else. It costs nothing to look at — it is the last line on the page. */
.ac-fine{margin-bottom:calc(var(--tap) - 1.2em)}

/* Three corrections found by looking at the rendered page rather than at the
   markup, which is the only way any of them shows up.

   1. TWO GROUPS OF ROWS DREW TWO HAIRLINES. The last row of a group closes it
      with a border-bottom and the first row of the next opens with a
      border-top, so /settings had a doubled line — with an 8px gap in it —
      between Email and Subscription. Adjacent groups share one line.
   2. THE EYEBROW AFTER THE VISUALLY-HIDDEN HEADING. `:first-child` cannot
      match it: h2.jn-vh is the first child of .ac-body and takes up no space,
      so the first eyebrow on a screen was carrying the 30px meant to separate
      one section from the next.
   3. THE PADLOCK ON THE SERIES COVER. app.css's .lock is 26px, drawn for a
      148px shelf cover. On this row's 74px plate it covered a third of the
      painting. Same treatment css/today.css gives .tdy-serie, for the same
      reason and at the same size. */
.ac-rows + .ac-rows{margin-top:0}
.ac-rows + .ac-rows > .ac-row:first-child{border-top:0}
.ac-body > h2 + .ac-eyebrow{margin-top:22px}
.ac-serie .lock{right:4px; top:4px; width:20px; height:20px; font-size:.58rem}
