

  /* dark, the same roles in deep water */
  

  /* legacy names still used in a few places, mapped rather than hunted down */
  :root, 

*{ box-sizing:border-box; margin:0; padding:0; -webkit-tap-highlight-color:transparent; }
/* Task 13.4. The document itself never scrolls, so the only thing overscroll
   can produce here is the two gestures that say "this is a web page": the
   rubber-band at the top of the screen and Chrome's pull-to-refresh, which in
   an app reloads the thing somebody is in the middle of. `none` rather than
   `contain`, because contain stops the chaining and keeps the bounce, and the
   bounce is half of what is being read. Sheets keep `contain` on purpose:
   they scroll inside themselves and must not drag the screen behind them. */
html, body{ height:100%; overscroll-behavior:none; }
body{
    font-family:var(--font-ui);
    background:var(--ground);
    background-attachment:fixed;
    color:var(--ink);
    -webkit-font-smoothing:antialiased;
    text-rendering:optimizeLegibility;
    overflow:hidden;
    /* svh, not dvh. dvh tracks whatever the browser chrome is doing, and on
       Android Chrome the first layout can resolve it against the large
       viewport before the address bar has settled. The shell is then taller
       than what is visible, and because the root does not scroll there is no
       way to reach the bottom of it: the tab bar sat off the screen until the
       page was reloaded, which is the whole of that bug.

       svh is the viewport with the browser chrome fully expanded, which is the
       smallest it ever gets, so nothing can overflow at any moment of the
       load. It is also exact rather than merely safe here, because a root that
       never scrolls never lets Android retract the address bar.

       height:100% first, so a browser without svh still gets a full-height
       shell rather than an auto one. */
    height:100%;
    height:100svh;
    user-select:none;
  }
/* Task 13.4. The body turns selection off because a long press on a row, a
   heading or a tab label is a press, not the start of a copy, and the blue
   sweep and the two handles that answer it are the tell people describe as
   "it's just the website".

   Text that is somebody's own content is the exception, and it has to be
   stated rather than inherited: a control inside a `user-select:none` subtree
   is not reliably selectable in WebKit, which is how an input can stop
   accepting a double-tap-to-select-a-word. Inputs so a person can correct what
   they typed, .selectable so a task's own name can be copied out of the sheet
   that shows it. */
input, textarea, select, [contenteditable="true"], .selectable{
    -webkit-user-select:text; user-select:text;
  }
button{ font-family:inherit; cursor:pointer; border:none; background:none; color:inherit; }
a{ color:inherit; text-decoration:none; }
input, select{ font-family:inherit; font-size:var(--fs-4); color:var(--ink); }

:root{ --nav-h: 58px; }
#app{ height:100%; height:100svh; display:flex; flex-direction:column; max-width:520px; margin:0 auto; position:relative; }

  /* ---------- Header ---------- */
#app header{
    padding:calc(var(--safe-top) + var(--space-5)) var(--space-5) var(--space-3);
    display:flex; align-items:center; justify-content:space-between;
    flex-shrink:0;
  }
.brand{ display:flex; align-items:center; gap:var(--space-2); }
.brand svg{ width:22px; height:22px; display:block; }
  /* The wordmark, the one place the display face appears in the app. Set
     at 600 with tight tracking, the way the brand draws it. */
.brand span{
    font-family:var(--display);
    font-size:var(--fs-4); font-weight:var(--fw-hi);
    letter-spacing:-0.02em; color:var(--ink);
  }
.coins{
    display:flex; align-items:center; gap:var(--space-2);
    background:var(--surface); border:1px solid var(--line);
    padding:var(--space-2) var(--space-3) var(--space-2) var(--space-3); border-radius:var(--r-pill);
    font-family:var(--font-num);
    font-variant-numeric:tabular-nums; font-weight:var(--fw-hi); font-size:var(--fs-3);
    transition:transform var(--dur-base) var(--ease), border-color var(--dur-slow) var(--ease);
  }
.coins.bump{ transform:scale(1.14); border-color:var(--line); }
.coin-dot{ width:15px; height:15px; border-radius:50%; background:var(--surface-2); }

  /* ---------- Main scroll area ---------- */
  /* The last card on every screen was running under the tab bar and could not
     be scrolled clear of it. Bottom padding is the bar height plus its safe-area
     inset plus one step, so the final element always clears.
     --nav-h is declared here rather than in tokens.css because it is a measured
     consequence of the nav rule below, not a design decision anyone makes. */
  /* A column, not a block.
   *
   * Every screen was a flat run of siblings in source order, and that had two
   * consequences. Nothing owned the space between them, so 42 margin-top
   * inline styles did it one element at a time. And `order` did nothing:
   * .mode-strip carries `order:99` so the extension advert sits at the end of
   * a screen, a rule a test asserts is present, and on a block parent it has
   * no effect at all. The advert was therefore the first thing on Now, Plan,
   * Story and Challenges, above the greeting.
   *
   * flex:1 and overflow-y:auto are unchanged and are asserted at both widths. */
#app main{
    flex:1; overflow-y:auto; overflow-x:hidden; -webkit-overflow-scrolling:touch;
    display:flex; flex-direction:column; align-items:stretch;
    padding:var(--space-1) var(--space-5)
            calc(var(--nav-h) + max(var(--space-2), var(--safe-bottom)) + var(--space-4));
    /* Task 13.4, the same reason as html and body above: `contain` already
       stopped the scroll chaining out of here, but the scroller itself still
       bounced at both ends. `none` stops both, so the end of a list is the end
       of a list. */
    overscroll-behavior:none;
  }
  /* The gap belongs to .page, not here: an unmigrated screen still carries its
     own margins and a gap on the parent would double every one of them. */
#app main > .page{ flex:0 0 auto; }
main::-webkit-scrollbar{ display:none; }


  /* ---------- Now: the single next action ---------- */
.nextlabel{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-mute); margin-bottom:var(--space-3); display:flex; align-items:center; gap:var(--space-2); }
.nextlabel .why{ color:var(--ink-soft); }

.nowcard{
    background:var(--surface);
    border-top:1px solid var(--line);
    border-radius:var(--r-card);
    padding:var(--space-5) var(--space-5) var(--space-5);
    position:relative; overflow:hidden;
    animation:rise var(--dur-base) var(--ease) both;
  }
.task-cat{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--tide); margin-bottom:var(--space-2); letter-spacing:.01em; }
.task-name{ font-size:var(--fs-6); font-weight:var(--fw-hi); line-height:1.14; letter-spacing:-0.03em; text-wrap:balance; margin-bottom:var(--space-4); }
.task-meta{ display:flex; gap:var(--space-4); flex-wrap:wrap; color:var(--ink-soft); font-size:var(--fs-3); font-weight:var(--fw-med); margin-bottom:var(--space-5); }
.task-meta b{ color:var(--ink); font-weight:var(--fw-hi); font-variant-numeric:tabular-nums; }
.forward{
    font-size:var(--fs-2); color:var(--ink-soft); line-height:1.45;
    border-left: var(--band-w) solid var(--tide);
    padding:var(--space-3) var(--space-3) var(--space-3) var(--space-4); margin-bottom:var(--space-5); font-weight:var(--fw-med);
  }
.forward b{ color:var(--tide); font-weight:var(--fw-hi); }

  /* session-length quick pick */
.lenpick{ display:flex; gap:var(--space-2); margin-top:var(--space-4); justify-content:center; }
.lenpick button{
    flex:1; padding:var(--space-2); border-radius:var(--r-control); background:var(--surface); border:1px solid var(--line);
    color:var(--ink-soft); font-size:var(--fs-3); font-weight:var(--fw-hi); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease);
  }
.lenpick button.on{ background:var(--surface-2); border-color:var(--line); color:var(--ink-soft); }
.lenpick button:focus-visible{ box-shadow:var(--focus-ring); outline:none; }
@media (hover:hover) and (pointer:fine){
  .lenpick button:hover{ background:var(--surface-2); border-color:var(--line-soft); }
}

  /* ---------- Empty state (onboarding) ---------- */
  /* Task 3.5 left-aligned these. The alignment and the padding are decided in
     css/world.css, which carries the same selector later in the cascade and
     wins, so neither is restated here; what is left is the entrance and the
     type. The paragraph's own `margin:0 auto` had to go either way: an auto
     side margin centres the block inside its measure whatever the parent's
     text-align says, so without that change the sentence would have stayed in
     the middle of the screen while reading ragged-right. */
.empty{ animation:rise var(--dur-base) var(--ease) both; }
.empty .mark{ width:64px; height:64px; margin:0 auto var(--space-5); opacity:.9; }
.empty h2{ font-size:var(--fs-5); font-weight:var(--fw-med); letter-spacing:-0.015em; margin-bottom:var(--space-2); }
.empty p{ color:var(--ink-soft); font-size:var(--fs-3); line-height:1.5; max-width:34ch; margin:0 0 var(--space-5); }

  /* ---------- Tasks view ---------- */
  /* A heading has to outrank the thing it introduces. This was --fs-3 at
     --ink-mute, which is the same size as body copy and greyer than it, so
     every section read as a caption and the page read as fragments. */
.section-h{
    font-size:var(--fs-4); font-weight:var(--fw-med); color:var(--ink);
    letter-spacing:-0.01em;
    margin:var(--space-6) var(--space-1) var(--space-3);
  }
.section-h:first-child{ margin-top:var(--space-1); }
.tasklist{ display:flex; flex-direction:column; gap:var(--space-2); }
.taskrow{
    background:var(--surface); border:1px solid var(--line); border-radius:var(--r-control);
    padding:var(--space-4) var(--space-4); display:flex; align-items:center; gap:var(--space-3);
    animation:rise var(--dur-base) var(--ease) both;
  }
.prio{ display:flex; flex-direction:column; gap:var(--space-1); flex-shrink:0; }
.prio i{ width:6px; height:6px; border-radius:50%; background:var(--line); }
.prio.p1 i:nth-child(1), .prio.p2 i:nth-child(-n+2), .prio.p3 i{ background:var(--ink-soft); }
.taskrow .body{ flex:1; min-width:0; }
.taskrow .nm{ font-size:var(--fs-4); font-weight:var(--fw-hi); letter-spacing:-0.01em; margin-bottom:var(--space-1); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.taskrow .sub{ font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); }
.taskrow .sub .due{ color:var(--tide); }
.win-urgent{ color:var(--ink-soft); font-weight:var(--fw-hi); }
.taskrow .del{ flex-shrink:0; width:30px; height:30px; border-radius:var(--r-control); color:var(--ink-mute); display:grid; place-items:center; transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.taskrow .del:active{ background:var(--surface); color:var(--ink-soft); }
.taskrow.done{ opacity:.5; }
.taskrow.done .nm{ text-decoration:line-through; text-decoration-color:var(--ink-mute); }

  /* ---------- Story / life memory ---------- */
.story-intro{ font-size:var(--fs-3); color:var(--ink-soft); font-weight:var(--fw-med); margin:var(--space-1) 0 var(--space-4); line-height:1.5; }

  /* focus room (evolving avatar) */
.room{
    margin:var(--space-2) 0 var(--space-1); border-radius:var(--r-card); overflow:hidden;
    background:var(--ink);
    border:1px solid var(--line);
    animation:rise var(--dur-base) var(--ease) both;
  }
.room svg{ display:block; }
.room-cap{ padding:var(--space-3) var(--space-4) var(--space-4); border-top:1px solid var(--line); background:var(--surface); color:var(--ink); }
.room-title{ font-size:var(--fs-3); font-weight:var(--fw-hi); letter-spacing:-0.01em; }
.room-next{ font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); margin-top:var(--space-1); line-height:1.45; }
/* The stat tile is gone. Insights and Story set their numbers with .metric,
   which is a figure and its label rather than a tinted box in a grid. */


.levelwrap{ margin:var(--space-4) 0 var(--space-1); }
.levelwrap .top{ display:flex; justify-content:space-between; align-items:baseline; margin-bottom:var(--space-2); }
.levelwrap .lv{ font-size:var(--fs-4); font-weight:var(--fw-hi); }
.levelwrap .xp{ font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); font-variant-numeric:tabular-nums; }
.bar{ height:9px; border-radius:var(--r-pill); background:var(--surface); overflow:hidden; border:1px solid var(--line); }
.bar i{ display:block; height:100%; border-radius:var(--r-pill); background:var(--tide); transition:width var(--dur-base) var(--ease); }
.bar.sk{ height:7px; margin-top:var(--space-2); }

  /* skills panel */
.skills{ display:flex; flex-direction:column; gap:var(--space-3); }
.skill-top{ display:flex; align-items:center; justify-content:space-between; }
.skill-nm{ display:flex; align-items:center; gap:var(--space-2); font-size:var(--fs-3); font-weight:var(--fw-hi); }
.skill-lv{ font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); font-variant-numeric:tabular-nums; }

  /* insights */
.insight-body{ font-size:var(--fs-3); color:var(--ink-soft); font-weight:var(--fw-med); line-height:1.5; }
.forecast{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-control); padding:var(--space-4) var(--space-4); animation:rise var(--dur-base) var(--ease) both; }
.fc-big{ font-size:var(--fs-6); font-weight:var(--fw-hi); letter-spacing:-0.03em; color:var(--ink-soft); font-variant-numeric:tabular-nums; line-height:1; }
.fc-big small{ font-size:var(--fs-4); font-weight:var(--fw-hi); color:var(--ink-soft); letter-spacing:normal; }
.fc-lbl{ font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); margin-top:var(--space-2); line-height:1.45; }

.plan{ display:flex; flex-direction:column; gap:var(--space-2); }
.pday{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-control); padding:var(--space-3) var(--space-4); animation:rise var(--dur-base) var(--ease) both; }
.pday.today{ background:var(--surface); border-color:var(--line); }
.pday.empty{ padding:var(--space-3) var(--space-4); display:flex; align-items:center; justify-content:space-between; opacity:.55; }
.pd-head{ display:flex; align-items:baseline; justify-content:space-between; margin-bottom:var(--space-2); }
.pd-date{ font-size:var(--fs-3); color:var(--ink-mute); font-weight:var(--fw-med); }
.pd-date b{ color:var(--ink); font-weight:var(--fw-hi); font-size:var(--fs-3); }
.pday.today .pd-date b{ color:var(--ink-soft); }
.pd-load{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-soft); font-variant-numeric:tabular-nums; }
.pd-bar{ height:4px; border-radius:var(--r-pill); background:var(--surface); overflow:hidden; margin-bottom:var(--space-3); }
.pd-bar i{ display:block; height:100%; border-radius:var(--r-pill); background:var(--surface-2); }
/* The planner's routine row. It carries .routines too now, so the chips inside
   are the same control as the ones on Now rather than a lookalike that did
   nothing when tapped. This only handles the spacing around them. */
  .pd-routines{ display:flex; gap:var(--space-2); flex-wrap:wrap; margin-bottom:var(--space-3); }
.pd-list{ display:flex; flex-direction:column; gap:var(--space-2); }
.pd-task{ display:flex; align-items:center; gap:var(--space-2); }
.pdot{ width:7px; height:7px; border-radius:50%; flex-shrink:0; }
.pd-nm{ font-size:var(--fs-3); font-weight:var(--fw-med); flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:left; padding:0; }
.pd-meta{ font-size:var(--fs-1); color:var(--ink-mute); font-weight:var(--fw-med); flex-shrink:0; }
.aisum{ font-size:var(--fs-3); color:var(--ink-soft); line-height:1.55; margin:var(--space-1) 0 var(--space-4); text-wrap:pretty; }
.scores{ display:flex; flex-direction:column; gap:var(--space-4); }
.sc-top{ display:flex; align-items:baseline; justify-content:space-between; margin-bottom:var(--space-2); }
.sc-lbl{ font-size:var(--fs-3); font-weight:var(--fw-hi); letter-spacing:-0.01em; }
.sc-val{ font-size:var(--fs-4); font-weight:var(--fw-hi); color:var(--ink-soft); font-variant-numeric:tabular-nums; }
.boosts{ display:flex; flex-direction:column; gap:var(--space-2); }
.boost{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-control); padding:var(--space-3) var(--space-4); }
.bo-t{ font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink-soft); margin-bottom:var(--space-1); }
.outlooks{ display:flex; flex-direction:column; gap:var(--space-2); }
.outlook{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-control); padding:var(--space-3) var(--space-4); animation:rise var(--dur-base) var(--ease) both; }
.outlook.warn{ background:var(--surface-2); border-color:var(--line); }
.ol-nm{ font-size:var(--fs-3); font-weight:var(--fw-hi); letter-spacing:-0.01em; margin-bottom:var(--space-1); }
.ol-st{ font-size:var(--fs-2); color:var(--ink-soft); font-weight:var(--fw-med); }
.outlook.warn .ol-st{ color:var(--ink-soft); }

.rhythm{ display:flex; align-items:flex-end; justify-content:space-between; gap:var(--space-2); height:80px; margin-top:var(--space-3); padding:0 var(--space-1); }
.rcol{ flex:1; display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:var(--space-2); height:100%; }
.rbar{ width:100%; max-width:44px; border-radius:var(--r-control); background:var(--surface-2); transition:height var(--dur-base) var(--ease), opacity var(--dur-slow) var(--ease); }
.rlbl{ font-size:var(--fs-1); color:var(--ink-mute); font-weight:var(--fw-med); }

/* The same strip, over the hours of the clock rather than over four words.
   Up to twenty columns instead of four, so they are thinner, they sit tighter,
   and the labels stop being centred under a wide bar and start being ticks on
   an axis. Nothing else about the material changes. */
.rhythm--hours{ gap:2px; padding:0; }
.rhythm--hours .rcol{ gap:var(--space-1); }
/* A column two hours wide can carry a 3px corner. One this thin cannot: the
   radius eats the bar. --r-card is zero, which is the rule the rest of the
   system is drawn with anyway. */
.rhythm--hours .rbar{ max-width:none; border-radius:var(--r-card); }
/* Only every third hour is labelled, so the axis has room at the ordinary
   smallest size and does not need a font size of its own. */
.rhythm--hours .rlbl{ line-height:1; min-height:1em; }

.miles{ display:flex; flex-direction:column; gap:var(--space-2); }
.mile{
    display:flex; align-items:center; gap:var(--space-3); padding:var(--space-3) var(--space-4);
    background:var(--surface); border:1px solid var(--line); border-radius:var(--r-control);
    animation:rise var(--dur-base) var(--ease) both;
  }
.mile .ic{ width:34px; height:34px; border-radius:var(--r-control); background:var(--surface-2); display:grid; place-items:center; flex-shrink:0; }
.mile .ic svg{ width:18px; height:18px; color:var(--ink-soft); }
.mile .t{ font-size:var(--fs-3); font-weight:var(--fw-hi); }
.mile .d{ font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); margin-top:var(--space-1); }

.feed{ display:flex; flex-direction:column; gap:var(--space-1); }
.fitem{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-3) var(--space-1); border-bottom:1px solid var(--line); }
.fitem:last-child{ border-bottom:none; }
.fitem .fdot{ width:8px; height:8px; border-radius:50%; flex-shrink:0; }
.fitem .ft{ flex:1; font-size:var(--fs-3); font-weight:var(--fw-med); }
.fitem .ft b{ font-weight:var(--fw-hi); }
.fitem .fm{ font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); font-variant-numeric:tabular-nums; }

  /* ---------- Bottom nav ---------- */
  /* Solid, with a hairline. Not a floating island: a rounded bar with a shadow
     reads as a card that happens to contain navigation, and it competes with the
     content above it for the same visual weight. */
#app nav{
    /* flex-shrink and an explicit floor: the bar is the one thing that must
       survive any layout mistake, because without it there is no way to move
       between screens at all.

       --surface-solid, not --surface: in light mode --surface is pure white
       and the page is one step below it, so a white bar separated by a hairline
       reads as absent. This is chrome, not content, and it has to announce
       itself. The same treatment was set in primitives.css and lost, because
       that file loads first. */
    flex-shrink:0; display:flex; min-height:var(--nav-h);
    /* One rule along the top edge. --shadow-2 was appended here as well, and
       --shadow-2 is a hairline ring on all four sides, so the bar carried a
       line down its left, right and bottom edges into the safe area. */
    box-shadow:0 -1px 0 var(--line);
    /* Tightened when the sixth tab arrived. Six labels across a 375px phone
       leaves 54px each, and the horizontal padding was the cheapest 32px to
       give back. */
    padding:var(--space-2) var(--space-2) max(var(--space-2), var(--safe-bottom));
    background:var(--surface-solid);
    border-top:1px solid var(--line);
    gap:var(--space-1);
  }
#app nav button{
    flex:1; padding:var(--space-2) 0 var(--space-2); border-radius:var(--r-control); display:flex; flex-direction:column; align-items:center; gap:var(--space-1);
    color:var(--ink-mute); transition:color var(--dur-base) var(--ease);
  }
#app nav button svg{ width:24px; height:24px; }
#app nav button span{
    font-size:0.6875rem; font-weight:var(--fw-med); letter-spacing:0;
    /* Never truncated. A tab label that says "Insigh..." is worse than a
       smaller one that says "Insights". */
    white-space:nowrap; overflow:visible;
  }
  /* Six tabs on a narrow phone. The rule above is that a label is never
     truncated, so the label gets smaller rather than shorter: "Challenges" at
     11px needs about 57px and there are 54px to give. Ten reads fine at this
     weight and keeps the word whole, which is the point. */
@media (max-width: 420px){
#app nav button span{ font-size:0.625rem; }
#app nav button svg{ width:22px; height:22px; }
  }
#app nav button.on{
    color:var(--ink);
    background:none;
    /* Weight as well as colour. The rail was fixed during the regression
       reversal and this was left saying it in colour alone, which is the one
       signal that disappears for anyone who cannot see it. */
    font-weight:var(--fw-hi);
  }

  /* Four tabs. Task 2.3, UI-SPEC section 2, behind flags.navFour: the frame
     sets data-four on the bar when it paints the four.

     Each tab carries an outline icon and a filled one and only one shows. The
     switch from outline to filled, with the label going from --ink-mute to
     --ink, is the entire active treatment: no pill, no band, no accent. The
     phone rules in material.css and outlined.css say the six-tab bar's active
     state with a fill and an accent band; specificity beats them here rather
     than position, so the six keep theirs while the flag is off. */
#app nav .nav-ic--f{ display:none; }
#app nav button.on .nav-ic--o{ display:none; }
#app nav button.on .nav-ic--f{ display:block; }
@media (max-width: 879px){
#app nav[data-four] button,
#app nav[data-four] button.on{ background:none; box-shadow:none; }
#app nav[data-four] button{ color:var(--ink-mute); }
#app nav[data-four] button.on,
#app nav[data-four] button.on svg,
#app nav[data-four] button.on span{ color:var(--ink); }
#app nav[data-four] button svg{ width:17px; height:17px; }
  /* Four labels fit a 360px phone at reading size, so the step down that six
     needed does not apply. */
#app nav[data-four] button span{ font-size:0.6875rem; }
  }

  /* ---------- Session takeover ---------- */
.session{
    position:fixed; inset:0; z-index:var(--z-scrim);
    background:var(--ground);
    display:flex; flex-direction:column; align-items:center;
    padding:calc(var(--safe-top) + var(--space-6)) var(--space-5) calc(var(--safe-bottom) + var(--space-5));
    max-width:520px; margin:0 auto;
    animation:sessIn var(--dur-base) var(--ease) both;

    /* Scrollable, because it stopped fitting.
     *
     * This was a fixed panel that assumed its contents would always be shorter
     * than the screen. Then a long task name wrapped to three lines, the sound
     * picker arrived above the ring, and the reminder button below it, and
     * Pause and Finish went off the bottom of an iPhone with no way to reach
     * them: the only two controls that end a session, on the screen you cannot
     * leave any other way.
     *
     * Momentum scrolling is named explicitly because iOS gives a fixed element
     * the abrupt kind otherwise. */
    overflow-y:auto;
    -webkit-overflow-scrolling:touch;
    overscroll-behavior:contain;
  }
  /* The ring may shrink, but never below something you can read at a glance. */
.session .ring-wrap{ flex:0 0 auto; }
@keyframes sessIn{from{ opacity:0; transform:scale(1.04); }to{ opacity:1; transform:scale(1); } }
.session.out{ animation:sessOut var(--dur-base) var(--ease) both; }
@keyframes sessOut{to{ opacity:0; transform:scale(1.03); } }
.sess-cat{ font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--tide); margin-bottom:var(--space-2); }
.sess-name{ font-size:var(--fs-5); font-weight:var(--fw-hi); letter-spacing:-0.025em; text-align:center; text-wrap:balance; margin-bottom:var(--space-5); max-width:20ch; flex:0 0 auto; }

.ring-wrap{ position:relative; width:270px; height:270px; margin:var(--space-4) 0; display:grid; place-items:center; }
.ring-wrap svg{ transform:rotate(-90deg); position:absolute; inset:0; }
.ring-bg{ fill:none; stroke:var(--surface); stroke-width:10; }
.ring-fg{ fill:none; stroke:url(#rg); stroke-width:10; stroke-linecap:round; transition:stroke-dashoffset 1s linear; }
.ring-inner{ text-align:center; }
.timer{ font-family:var(--font-num); font-size:var(--fs-numeric); font-weight:var(--fw-body); letter-spacing:-0.03em; font-variant-numeric:tabular-nums; line-height:1; }
.timer-sub{ font-size:var(--fs-3); color:var(--ink-mute); font-weight:var(--fw-med); margin-top:var(--space-2); }
.sess-calm{ font-size:var(--fs-3); color:var(--ink-soft); font-weight:var(--fw-med); text-align:center; max-width:26ch; margin:var(--space-5) auto 0; line-height:1.5; text-wrap:balance; }
.sess-ot{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--tide); text-align:center; margin-top:var(--space-3); letter-spacing:.02em; }
.sess-bg{ font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); text-align:center; margin:var(--space-3) auto 0; }

  /* Pinned to the bottom of the flow and given breathing room beneath, so that
     on a short screen the last thing you scroll to is the pair of buttons that
     end the session rather than half of one. */
.sess-actions{ display:flex; gap:var(--space-3); width:100%; margin-top:var(--space-5); flex:0 0 auto; padding-bottom:var(--space-4); }
.sess-actions button{ flex:1; min-height:48px; padding:var(--space-4); border-radius:var(--r-control); font-size:var(--fs-4); font-weight:var(--fw-hi); transition:transform var(--dur-base) var(--ease), background var(--dur-base) var(--ease); }
.sess-actions button:active{ transform:scale(.96); }
/* Continue later (session/later.js): one line under the paused controls. */
.sess-later{ width:100%; max-width:360px; margin:var(--space-3) auto 0; }
.sess-later-note{ text-align:center; margin:var(--space-1) 0 0; }
/* Catching up on opening (platform/synclock.js): the app held for a moment
   while another device's changes come in. The bar is machine state, so tide,
   and it breathes on the product's own pulse rather than a motion of its own. */
.synclock{ position:fixed; inset:0; z-index:calc(var(--z-sheet) + 10); display:grid; place-items:center;
  background:color-mix(in oklab, var(--ground) 97%, transparent); padding:var(--space-5); }
.synclock-card{ text-align:center; max-width:320px; }
.synclock-bar{ width:120px; height:3px; margin:0 auto var(--space-4); border-radius:var(--r-pill);
  background:var(--line-soft); overflow:hidden; }
.synclock-bar i{ display:block; height:100%; background:var(--tide); animation:pulse 1.2s var(--ease) infinite; }
.synclock-t{ font-size:var(--fs-4); font-weight:var(--fw-med); color:var(--ink); margin:0; }
.synclock-s{ font-size:var(--fs-2); color:var(--ink-mute); margin:var(--space-1) 0 0; }
@media (prefers-reduced-motion: reduce){ .synclock-bar i{ animation:none; } }
[data-motion="still"] .synclock-bar i{ animation:none; }
/* The licence check (platform/license.js, D139): Sailyn asks to come online
   once its plan has gone ten days unchecked. Opaque, because the point is that
   the app is held; the help button stays, because the crisis path is never
   switched off, and its sheet opens above this (under the scrim's layer). */
.licgate{ position:fixed; inset:0; z-index:calc(var(--z-scrim) - 1); display:grid; place-items:center;
  background:var(--ground); padding:var(--space-5); }
.licgate-card{ text-align:center; max-width:340px; display:flex; flex-direction:column; gap:var(--space-3); }
.licgate-t{ font-size:var(--fs-5); font-weight:var(--fw-hi); color:var(--ink); margin:0; }
.licgate-s{ font-size:var(--fs-3); color:var(--ink-soft); margin:0; line-height:1.5; }
.licgate-m{ font-size:var(--fs-2); color:var(--ink-mute); margin:0; min-height:1.4em; }
.licgate-card .btn{ width:100%; min-height:48px; }

  /* ---------- Reward moment ---------- */
.reward{
    position:fixed; inset:0; z-index:var(--z-sheet); display:grid; place-items:center;
    pointer-events:none; padding:var(--space-6);
  }
.reward-card{
    text-align:center; transform:scale(.7); opacity:0;
    animation:rewardPop var(--dur-base) var(--ease) forwards;
  }
@keyframes rewardPop{
0%{ transform:scale(.6); opacity:0; }
18%{ transform:scale(1.08); opacity:1; }
30%{ transform:scale(1); }
78%{ transform:scale(1); opacity:1; }
100%{ transform:scale(.96); opacity:0; }
  }
.reward-glow{ position:absolute; width:340px; height:340px; left:50%; top:50%; transform:translate(-50%,-50%); border-radius:50%;
    background:var(--surface-2); animation:glowPulse var(--dur-base) var(--ease) forwards; }
@keyframes glowPulse{0%{ transform:translate(-50%,-50%) scale(.4); opacity:0; }40%{ opacity:1; }100%{ transform:translate(-50%,-50%) scale(1.3); opacity:0; } }
.reward-amt{ font-family:var(--font-num); font-size:var(--fs-numeric); font-weight:var(--fw-hi); letter-spacing:-0.04em; color:var(--ink-soft); line-height:1; position:relative; display:flex; align-items:center; justify-content:center; gap:var(--space-3); }
.reward-amt .coin-dot{ width:40px; height:40px; }
.reward-msg{ font-size:var(--fs-4); font-weight:var(--fw-hi); color:var(--ink); margin-top:var(--space-4); position:relative; text-wrap:balance; }
.reward-sub{ font-size:var(--fs-3); color:var(--ink-soft); margin-top:var(--space-1); font-weight:var(--fw-med); position:relative; }
.spark{ position:absolute; width:7px; height:7px; border-radius:50%; background:var(--surface-2); left:50%; top:50%; }

  /* ---------- Sheet (add task) ---------- */
.scrim{
    position:fixed; inset:0; z-index:var(--z-scrim);
    background:var(--scrim);
    -webkit-backdrop-filter:blur(20px) saturate(140%);
    backdrop-filter:blur(20px) saturate(140%);
    opacity:0; transition:opacity var(--dur-base) var(--ease);
  }
.scrim.show{ opacity:1; }
.sheet{
    position:fixed; left:0; right:0; bottom:0; z-index:var(--z-scrim); max-width:520px; margin:0 auto;
    background:var(--surface); border-top:1px solid var(--line);
    border-radius:var(--r-card); padding:var(--space-2) var(--space-5) calc(var(--safe-bottom) + var(--space-5));
    transform:translateY(100%); transition:transform var(--dur-base) var(--ease);
    max-height:88dvh; overflow-y:auto; overscroll-behavior:contain; -webkit-overflow-scrolling:touch;
  }
.sheet::-webkit-scrollbar{ display:none; }
.sheet-x{
    position:sticky; top:0; z-index:2; float:right; width:34px; height:34px; border-radius:50%;
    background:var(--surface); border:1px solid var(--line); color:var(--ink-soft);
    display:grid; place-items:center; margin:-var(--space-1) -var(--space-1) 0 0;
  }
.sheet-x svg{ width:16px; height:16px; }
.sheet-x:active{ background:var(--line); }
.sheet-x:focus-visible{ box-shadow:var(--focus-ring); outline:none; }
@media (hover:hover) and (pointer:fine){
  .sheet-x:hover{ background:var(--surface-2); border-color:var(--line-strong); }
}
.sheet.show{ transform:translateY(0); }
.grab{ width:38px; height:5px; border-radius:var(--r-pill); background:var(--line); margin:0 auto var(--space-4); flex-shrink:0; }
.sheet h3{ font-size:var(--fs-5); font-weight:var(--fw-hi); letter-spacing:-0.02em; margin-bottom:var(--space-4); }
.field{ margin-bottom:var(--space-4); }
.field label{ display:block; font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-mute); margin-bottom:var(--space-2); }
.field .opt{ color:var(--ink-mute); font-weight:var(--fw-med); }
  /* Every input except the checkbox, rather than a list of types that has to
     be extended each time one is used. Naming them individually is why
     datetime-local, time and email rendered dark on dark: they inherited the
     ink colour and never got the background that goes with it. */
.field input:not([type=checkbox]):not([type=range]), .field select{
    width:100%; padding:var(--space-3) var(--space-4); border-radius:var(--r-control); background:var(--ground); border:1px solid var(--line); color:var(--ink);
    -webkit-appearance:none; appearance:none;
  }
.field input:focus-visible, .field select:focus-visible{ outline:none; border-color:var(--accent); }
.field input::placeholder{ color:var(--ink-mute); }
  /* The browser draws the calendar and clock glyphs in its own colour, which
     is near black and invisible on a dark field. */
  
.segrow{ display:flex; gap:var(--space-2); }
.segrow input[type=time]{ flex:1; padding:var(--space-3); border-radius:var(--r-control); background:var(--ground); border:1px solid var(--line); color:var(--ink); text-align:center; -webkit-appearance:none; }
.segrow button{
    flex:1; padding:var(--space-3); border-radius:var(--r-control); background:var(--ground); border:1px solid var(--line);
    color:var(--ink-soft); font-size:var(--fs-3); font-weight:var(--fw-med); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease);
  }
  /* Selected, said three ways.
     This was --surface-2 on a --ground base: a 0.022 lightness difference and
     a slightly darker label, which is no signal at all. The sound mode worked
     perfectly and looked like nothing was chosen.

     Fill, border and weight together, because any one of them alone is the
     one that disappears for somebody. */
.segrow button.on{
    background:var(--accent-wash);
    border-color:var(--accent);
    color:var(--accent-deep);
    font-weight:var(--fw-hi);
  }
.segrow button[aria-pressed="true"]{
    background:var(--accent-wash); border-color:var(--accent);
    color:var(--accent-deep); font-weight:var(--fw-hi);
  }


  /* ---------- toast ---------- */
.toast{
    position:fixed; left:50%; bottom:calc(var(--safe-bottom) + 92px); transform:translate(-50%, 20px);
    z-index:var(--z-sheet); background:var(--surface); border:1px solid var(--line);
    color:var(--ink); padding:var(--space-3) var(--space-4); border-radius:var(--r-pill); font-size:var(--fs-3); font-weight:var(--fw-med);
    opacity:0; transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); pointer-events:none; max-width:88vw; text-align:center;
  }
.toast.show{ opacity:1; transform:translate(-50%, 0); }

  /* Wider screens: the phone column looks lost in the middle of a laptop.
     Nav moves to a rail and the content gets room without becoming a wall. */
  /* ---- desktop: a real sidebar, not a phone stretched wide ----
     The brand sits at the top of the rail rather than floating across the
     content, which is what made the wide layout read as a scaled up phone. */
@media (min-width: 880px){
    
    /* 1180 was a 260px rail, a 760px column and 160px of nothing, on a window
       that is usually 1440 or wider. The shell holds the rail plus a measure
       plus a context column now, and the content actually uses it. */
#app{ max-width:1460px; flex-direction:row; align-items:stretch; gap:0; }
#app nav{
      order:-1; flex-direction:column; width:260px; flex-shrink:0; gap:var(--space-1);
      padding:calc(var(--rail-head) + var(--space-2)) var(--space-3) var(--space-4);
      background:var(--surface-2);
      border-right:1px solid var(--line); align-self:stretch;
    }
    /* absolute, not fixed. #app is centred at 1180px, so a fixed header
       anchors to the viewport and lands at the far left of the screen with
       the rail nowhere near it. #app is position:relative, so absolute puts
       it where the rail actually is. */
#app header{
      position:absolute; top:0; left:0; width:260px; height:var(--rail-head);
      justify-content:center; padding:var(--space-5) var(--space-4) var(--space-3);
      background:none; z-index:calc(var(--z-nav) + 1);
      flex-direction:column; align-items:flex-start; gap:var(--space-4);
    }
#app header .brand{ font-size:var(--fs-5); }
#app header .brand img{ width:28px; height:28px; }
#app header .hdr-right{ width:100%; justify-content:flex-start; gap:var(--space-2); }

    /* A real hit area, not a text link. 44px minimum, the label at reading
       size rather than the 11px a phone tab bar needs. */
#app nav button{
      flex:0 0 auto; flex-direction:row; justify-content:flex-start;
      gap:var(--space-3); min-height:44px;
      padding:var(--space-3) var(--space-3); border-radius:var(--r-control);
      transition:background var(--dur-base) var(--ease), color var(--dur-base) var(--ease);
    }
#app nav button span{ font-size:var(--fs-3); font-weight:var(--fw-med); }
#app nav button:hover{ background:var(--surface); color:var(--ink); }
#app nav button:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

    /* Where you are, said three ways: a filled surface, a weight change, and
       a rule down the leading edge. One of the three alone is what made the
       old state read as barely there, and colour alone is also the one that
       disappears for anyone who cannot see it. */
#app nav button.on{
      color:var(--ink);
      background:var(--surface);
      font-weight:var(--fw-hi);
      box-shadow:inset 2px 0 0 var(--accent);
    }
#app nav button.on svg{ color:var(--accent); }

#app main{ padding:var(--space-7) var(--space-7) var(--space-7); }
    /* A measure. Prose stops being readable somewhere past 80 characters, so
       everything is capped: .page at 820 above, and everything not yet
       composed into a page at the width it already had.
       Left-aligned rather than centred: the rail is the left edge of the
       product, and a column floating in the middle of the remaining space
       reads as a phone screenshot dropped onto a desktop. */
#app main > *{ max-width:760px; }
#app main > .page{ max-width:none; }
.plan{ display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:var(--space-3); align-items:start; }
.skills{ display:grid; grid-template-columns:1fr 1fr; gap:var(--space-4) var(--space-5); }
.scores{ display:grid; grid-template-columns:1fr 1fr; gap:var(--space-4) var(--space-5); }
.boosts{ display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); }
.routines{ gap:var(--space-2); }
.sheet{ max-width:560px; border-radius:var(--r-card); bottom:auto; top:50%; left:50%; right:auto;
      transform:translate(-50%,-46%) scale(.97); opacity:0; max-height:82dvh;
      border:1px solid var(--line); padding:var(--space-4) var(--space-6) var(--space-6); }
.sheet.show{ transform:translate(-50%,-50%) scale(1); opacity:1; }
.session{ max-width:100%; }
.room svg{ max-height:340px; }

    /* The measure. Prose and lists stop being readable somewhere around 80
       characters, so the primary column is capped and the rest of the width
       goes to the context column rather than to stretching a task name across
       a metre of screen. */
.page{ max-width:820px; }
  }

  /* ---- the second column ----
     A screen has a primary column and, where it genuinely has ambient content,
     a context column beside it. Ambient means: true all day, worth glancing
     at, never the thing you came to do. The shape of the day on Now, the goals
     on Plan, the rhythm on Insights.

     Below this width the two run in order in one column, which is why they are
     two elements in the markup rather than a duplicated block: the phone gets
     the same content in reading order and nothing is hidden from it. */
@media (min-width: 1160px){
.page--split{
      max-width:none;
      display:grid;
      grid-template-columns:minmax(0, 1fr) minmax(280px, 360px);
      column-gap:var(--space-7);
      align-items:start;
    }
    /* The rule between the columns does the separating, so the context column
       needs no fill and no box of its own. */
.page--split > .page-side{
      border-left:1px solid var(--line);
      padding-left:var(--space-6);
      position:sticky; top:0;
    }
  }
@media (min-width: 880px) and (max-width: 1100px){
#app main{ padding:var(--space-7) var(--space-5) var(--space-7); }
.plan{ grid-template-columns:minmax(0, 1fr); }
  }

  /* ---- tablet ----
     Between a phone and the rail there was nothing at all: #app stayed capped
     at 520px, so a 768 or 820 point tablet showed a phone column with 150px of
     empty page down each side and a bottom bar built for a thumb that is
     nowhere near it. The bar stays, because at this width it is still the
     right instrument, and the column stops pretending the screen is small.

     Decision D48: from 600, not 700. A folding phone opened flat is 673 to
     700 wide and a small tablet in portrait starts near 600, and below 700
     both still got the 520px phone column with the page showing down each
     side. Two more things the screenshots found. The 720px measure is for the
     words, and it capped the painting on Now as well, so on an iPad in
     portrait the harbour stopped 90px short of the right edge and the
     header's white icons sat past it on the white page, unseen; it runs edge
     to edge here as it does on a phone. And the four tabs, each capped at
     132px, packed to the left of the bar; they sit together in the middle. */
@media (min-width: 600px) and (max-width: 879px){
#app{ max-width:none; }
#app header{ padding-left:var(--space-6); padding-right:var(--space-6); }
#app main{ padding-left:var(--space-6); padding-right:var(--space-6); }
#app main > *{ max-width:720px; margin-inline:auto; }
#app #main[data-screen="now"] > .harbour--hero{
  max-width:none;
  margin-left:calc(-1 * var(--space-6)); margin-right:calc(-1 * var(--space-6));
}
#app nav{ padding-left:var(--space-6); padding-right:var(--space-6); justify-content:center; }
#app nav button{ max-width:132px; }
.page{ max-width:720px; }
  }

@media (prefers-reduced-motion: reduce){
*{ animation-duration:.01ms !important; transition-duration:.05ms !important; }
.ring-fg{ transition:none; }
  }

  /* header actions */
.hdr-right{ display:flex; align-items:center; gap:var(--space-2); }
.syncdot{ width:7px; height:7px; border-radius:50%; background:var(--ink-mute); transition:background var(--dur-slow) var(--ease); }
.syncdot.syncing{ background:var(--surface-2); animation:pulse 1.2s ease-in-out infinite; }
  /* Offline is the one state worth noticing from across the room, so it is the
     only dot that moves. */
.syncdot.offline{ background:var(--danger); animation:pulse 2.4s ease-in-out infinite; }
.gear{ width:38px; height:38px; border-radius:50%; display:grid; place-items:center; color:var(--ink-mute); border:1px solid transparent; transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.gear:active{ color:var(--ink-soft); background:var(--surface); }
.gear:focus-visible{ box-shadow:var(--focus-ring); outline:none; }
@media (hover:hover) and (pointer:fine){
  .gear:hover{ background:var(--surface); color:var(--ink-soft); }
}
.gear svg{ width:20px; height:20px; }

/* Theme and language, sitting next to the gear and built the same way, because
   they are the same kind of thing: small quiet controls that are always in the
   corner. The icon changes with the state, so it carries its own meaning and
   needs no label beside it. */
.themebtn, .langbtn{ width:38px; height:38px; border-radius:50%; display:grid; place-items:center;
  color:var(--ink-mute); background:transparent; border:1px solid transparent; cursor:pointer;
  transition: color var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease); }
.themebtn svg, .langbtn svg{ width:19px; height:19px; }
.themebtn:active, .langbtn:active{ color:var(--ink-soft); background:var(--surface); }
@media (hover:hover) and (pointer:fine){
  .themebtn:hover, .langbtn:hover{ color:var(--ink); background:var(--surface); }
}
.themebtn:focus-visible, .langbtn:focus-visible{ box-shadow:var(--focus-ring); outline:none; }
.coins:active{ transform:scale(.94); }

  /* language sheet */
.langlist{ display:flex; flex-direction:column; gap:var(--space-2); }
.langopt{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-3) var(--space-4);
  background:var(--ground); border:1px solid var(--line); border-radius:var(--r-control);
  color:var(--ink-soft); font-size:var(--fs-3); font-weight:var(--fw-med);
  transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.langopt:active{ background:var(--surface-2); }
@media (hover:hover) and (pointer:fine){
  .langopt:hover{ background:var(--surface); border-color:var(--line-strong); }
}
.langopt.on{ background:var(--surface-2); border-color:var(--line-strong); color:var(--ink); }
.langflag{ font-size:var(--fs-5); line-height:1; }
/* The row is a stack now: the language named in the reader's own words, and
   under it the name its own speakers use, which is the line somebody reads
   when they have landed somewhere they cannot read the first one. */
.langopt{ flex-direction:column; align-items:flex-start; gap:2px; text-align:left; }
.langname{ flex:none; }
.langendo{ font-size:var(--fs-1); color:var(--ink-mute); font-weight:var(--fw-body); }
.langopt.on .langendo{ color:var(--ink-soft); }
.sheet-hdr{ display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-4); position:sticky; top:0; background:var(--surface); z-index:2; padding-top:var(--space-2); margin-top:calc(-1 * var(--space-2)); }
.sheet-hdr h2{ font-size:var(--fs-5); font-weight:var(--fw-hi); letter-spacing:-0.02em; margin:0; }
.close-sheet{ width:34px; height:34px; border-radius:50%; background:transparent; border:1px solid var(--line);
  color:var(--ink-soft); display:grid; place-items:center; transition: color var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease); }
.close-sheet svg{ width:16px; height:16px; }
.close-sheet:active{ background:var(--line); }
.close-sheet:focus-visible{ box-shadow:var(--focus-ring); outline:none; }
@media (hover:hover) and (pointer:fine){
  .close-sheet:hover{ background:var(--surface-2); border-color:var(--line-strong); }
}
.sheet-body{ padding-bottom:var(--space-2); }


  /* shop */
.shop{ max-height:86dvh; overflow-y:auto; }
.shop::-webkit-scrollbar{ display:none; }
.shop-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-1); }
.shop-head h3{ margin:0; }
.shop-head .coins{ padding:var(--space-1) var(--space-3) var(--space-1) var(--space-2); font-size:var(--fs-3); }
.shop-sub{ font-size:var(--fs-2); color:var(--ink-mute); line-height:1.45; margin-bottom:var(--space-1); }
.shop-cur{ font-size:var(--fs-2); color:var(--ink-soft); margin-bottom:var(--space-3); }
.shop-cur b{ color:var(--ink); }
.linkbtn{ color:var(--ink-soft); font-weight:var(--fw-hi); font-size:var(--fs-2); }
.shop-list{ display:flex; flex-direction:column; gap:var(--space-2); }
.shop-item{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-3) var(--space-4); background:var(--ground); border:1px solid var(--line); border-radius:var(--r-control); }
.shop-item .sw{ width:20px; height:20px; border-radius:var(--r-control); flex-shrink:0; }
/* The default's swatch cannot be an inline literal like the others: it is
   whichever accent the token file and the current theme resolve to. */
.shop-item .sw--default{ background:var(--accent-brand); }   /* the shipped accent, since a worn theme overrides the live one */
.shop-nm{ flex:1; font-size:var(--fs-3); font-weight:var(--fw-med); }
/* The price is ink on the pill, with a coin in --gold, which is what has
   been earned. It was --accent-ink, the white made for text on an accent
   fill, left behind when the pill stopped being filled in the accent: white
   on --surface-2, so every price in the shop was unreadable. */
.shop-buy{ display:flex; align-items:center; gap:var(--space-1); padding:var(--space-2) var(--space-4); border-radius:var(--r-pill); background:var(--surface-2); color:var(--ink); font-size:var(--fs-3); font-weight:var(--fw-hi); font-variant-numeric:tabular-nums; }
.shop-buy::before{ content:""; width:12px; height:12px; border-radius:50%; background:var(--gold); }
.shop-buy:disabled{ background:var(--surface); color:var(--ink-mute); }
.shop-buy:disabled::before{ display:none; }
.shop-equip{ padding:var(--space-2) var(--space-4); border-radius:var(--r-pill); background:var(--surface); border:1px solid var(--line); color:var(--ink); font-size:var(--fs-3); font-weight:var(--fw-hi); }
.shop-have{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-mute); padding:var(--space-2) var(--space-1); }
.shop-have.on{ color:var(--ink-soft); }

  /* settings */
.dangerbtn{ width:100%; margin-top:var(--space-3); padding:var(--space-4); border-radius:var(--r-control); background:transparent; border:1px solid var(--surface-2); color:var(--danger); font-size:var(--fs-3); font-weight:var(--fw-hi); }
.dangerbtn:active{ background:var(--surface-2); }

  /* session share */
.sess-share{ display:flex; align-items:center; gap:var(--space-2); margin:var(--space-4) auto 0; padding:var(--space-2) var(--space-4); border-radius:var(--r-pill); background:var(--surface); border:1px solid var(--line); color:var(--ink-soft); font-size:var(--fs-2); font-weight:var(--fw-med); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.sess-share:active{ color:var(--ink); border-color:var(--tide); }

  /* onboarding */
.onboard{ position:fixed; left:50%; top:50%; transform:translate(-50%,-50%); z-index:var(--z-scrim); width:min(380px,90vw); background:var(--surface); border:1px solid var(--line); border-radius:var(--r-card); padding:var(--space-6) var(--space-5) var(--space-5); text-align:center; animation:rewardPop2 var(--dur-base) var(--ease) both; max-height:88dvh; overflow-y:auto; overscroll-behavior:contain; -webkit-overflow-scrolling:touch; }
.onboard::-webkit-scrollbar{ display:none; }
@keyframes rewardPop2{from{ opacity:0; transform:translate(-50%,-46%) scale(.94); }to{ opacity:1; transform:translate(-50%,-50%) scale(1); } }
.ob-ic{ width:60px; height:60px; border-radius:var(--r-card); background:var(--surface-2); display:grid; place-items:center; margin:0 auto var(--space-4); }
.ob-ic svg{ width:29px; height:29px; color:var(--ink-soft); }
.onboard h2{ font-size:var(--fs-5); font-weight:var(--fw-hi); letter-spacing:-0.025em; margin-bottom:var(--space-3); text-wrap:balance; line-height:1.2; }
.ob-body{ font-size:var(--fs-3); color:var(--ink-soft); line-height:1.55; margin:0 auto var(--space-5); max-width:30ch; text-wrap:pretty; }
/* What changed, under an update: the operator's own lines, kept as written. */
.updnotes{ text-align:left; margin:0 auto var(--space-5); max-width:34ch; max-height:12em; overflow:auto; border-top:1px solid var(--line); padding-top:var(--space-3); }
.updnotes h3{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink); margin:0 0 var(--space-2); }
.updnotes p{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.55; margin:0; white-space:pre-line; }
.ob-dots{ display:flex; gap:var(--space-2); justify-content:center; margin-bottom:var(--space-5); }
.ob-dots span{ width:6px; height:6px; border-radius:50%; background:var(--line); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.ob-dots span.on{ background:var(--surface-2); width:20px; border-radius:var(--r-pill); }

  /* auth */

  /* device setup */
.recipe{ text-align:left; margin:0 0 var(--space-3); padding-left:var(--space-4); display:flex; flex-direction:column; gap:var(--space-2); }
.recipe li{ font-size:var(--fs-3); color:var(--ink-soft); line-height:1.45; }
.copysteps{ width:100%; padding:var(--space-3); border-radius:var(--r-control); background:var(--ground); border:1px solid var(--line); color:var(--ink-soft); font-size:var(--fs-2); font-weight:var(--fw-med); margin-bottom:var(--space-3); }
.copysteps:active{ border-color:var(--ink-soft); color:var(--ink-soft); }
.recipe-note{ font-size:var(--fs-2); color:var(--ink-soft); font-weight:var(--fw-med); margin-bottom:var(--space-3); text-align:left; line-height:1.45; }
.setup-place{ font-size:var(--fs-2); color:var(--ink-soft); margin-bottom:var(--space-3); }
.setup-place b{ color:var(--ink-soft); }
.setup-count{ font-size:var(--fs-1); font-weight:var(--fw-hi); color:var(--ink-mute); letter-spacing:.02em; margin-bottom:var(--space-3); }
.setup-open{ display:block; width:100%; padding:var(--space-3); border-radius:var(--r-control); background:var(--ground);
    border:1px solid var(--line); color:var(--ink-soft); font-size:var(--fs-3); font-weight:var(--fw-hi);
    margin-bottom:var(--space-4); text-align:center; text-decoration:none;
    transition:transform var(--dur-base) var(--ease), background var(--dur-base) var(--ease); }
.setup-open:active{ transform:scale(.98); background:var(--surface-2); }
.setup-open:active{ transform:scale(.98); }
/* A shortcut that has been added: filled in the success colour, with a tick. */
.setup-open.is-done{ background:var(--good); border-color:var(--good); color:var(--ground);
    display:flex; align-items:center; justify-content:center; gap:var(--space-2); }
.setup-open .sc-tick{ flex:none; }

  /* pause reflection */
.preasons{ display:flex; flex-direction:column; gap:var(--space-2); margin-bottom:var(--space-5); }
.preason{ width:100%; padding:var(--space-3) var(--space-4); border-radius:var(--r-control); background:var(--ground); border:1px solid var(--line); color:var(--ink-soft); font-size:var(--fs-3); font-weight:var(--fw-med); text-align:left; transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.preason.on{ background:color-mix(in oklab, var(--tide) 28%, transparent); border-color:var(--tide); color:var(--tide); }
.pause-other{ display:flex; flex-direction:column; gap:var(--space-2); margin:0 0 var(--space-5); font-size:var(--fs-2); color:var(--ink-soft); }
.pause-need{ margin:var(--space-3) 0 0; font-size:var(--fs-2); color:var(--ink-mute); text-align:center; }
.holdbtn:disabled{ opacity:.55; cursor:not-allowed; }
.coachline{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2) var(--space-4); padding:var(--space-3) 0; margin-bottom:var(--space-4); border-bottom:1px solid var(--line); }
.coachline-txt{ flex:1 1 16rem; margin:0; font-size:var(--fs-2); color:var(--ink-soft); line-height:1.45; }
.coachline-txt strong{ color:var(--ink); font-weight:var(--fw-med); }
.coachline-acts{ display:flex; gap:var(--space-2); }

  /* daily routines strip */
.routines{ display:flex; gap:var(--space-2); margin-bottom:var(--space-4); flex-wrap:wrap; }
.rt{ display:flex; align-items:center; gap:var(--space-2); padding:var(--space-2) var(--space-4) var(--space-2) var(--space-3); border-radius:var(--r-pill); background:var(--surface); border:1px solid var(--line); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.rt-box{ width:17px; height:17px; border-radius:50%; border:1.8px solid var(--ink-mute); display:grid; place-items:center; flex-shrink:0; transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.rt-box svg{ width:10px; height:10px; color:var(--accent-ink); }
.rt:active{ transform:scale(.95); }
.rt.done{ background:var(--surface-2); border-color:var(--line); }
.rt.done .rt-box{ background:var(--surface-2); border-color:var(--line); }
  .rt.done .rt-nm{ color:var(--ink-soft); }

.blockbar{ width:100%; margin:var(--space-4) 0 var(--space-1); }
.blockbar.tight{ margin:0 0 var(--space-4); }
.bb-top{ display:flex; justify-content:space-between; font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); margin-bottom:var(--space-2); font-variant-numeric:tabular-nums; }
.blockbar .bar i{ background:var(--surface-2); }

  /* AI planner */
.plantext{ width:100%; padding:var(--space-4); border-radius:var(--r-control); background:var(--ground); border:1px solid var(--line); color:var(--ink); font-size:var(--fs-4); line-height:1.5; resize:none; -webkit-appearance:none; font-family:inherit; }
.plantext:focus-visible{ outline:none; border-color:var(--accent); }
.plantext::placeholder{ color:var(--ink-mute); }
.planex{ display:flex; flex-direction:column; gap:var(--space-2); margin:var(--space-3) 0 var(--space-1); }
.planex button{ text-align:left; padding:var(--space-2) var(--space-3); border-radius:var(--r-control); background:transparent; border:1px solid var(--line); color:var(--ink-mute); font-size:var(--fs-2); font-weight:var(--fw-med); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.planex button:active{ color:var(--ink-soft); border-color:var(--line); }
.planmsg{ font-size:var(--fs-3); color:var(--tide); line-height:1.5; margin:var(--space-3) 0; white-space:pre-wrap; min-height:2px; font-weight:var(--fw-med); }
.planmsg.err{ color:var(--danger); }
/* One question about what was just said (intentV2). The answers are ordinary
   buttons in a row that wraps, and the question reads as ink rather than as a
   machine-state tide: it is somebody being asked something. */
.planmsg.planask{ color:var(--ink); white-space:normal; }
.planask-q{ margin:0 0 var(--space-3); }
.planask-a{ display:flex; flex-wrap:wrap; gap:var(--space-2); align-items:center; }
.planask-time{ font-family:var(--font-num); font-variant-numeric:tabular-nums; }
.planctx{ margin-top:var(--space-4); border-top:1px solid var(--line); }
.planctx summary{ padding:var(--space-3) var(--space-1) var(--space-2); font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-mute); cursor:pointer; list-style:none; }
.planctx summary::-webkit-details-marker{ display:none; }

  /* manual */
.manual details{ border-bottom:1px solid var(--line); }
.manual details:last-child{ border-bottom:none; }
.manual summary{ padding:var(--space-4) var(--space-5) var(--space-4) var(--space-1); font-size:var(--fs-3); font-weight:var(--fw-hi); letter-spacing:-0.01em; cursor:pointer; list-style:none; position:relative; }
.manual summary::-webkit-details-marker{ display:none; }
.manual summary::after{ content:""; position:absolute; right:6px; top:21px; width:8px; height:8px; border-right:2px solid var(--ink-mute); border-bottom:2px solid var(--ink-mute); transform:rotate(45deg); transition:transform var(--dur-base) var(--ease); }
.manual details[open] summary{ color:var(--ink-soft); }
.manual details[open] summary::after{ transform:rotate(-135deg); border-color:var(--line); }
.man-a{ font-size:var(--fs-3); color:var(--ink-soft); line-height:1.6; padding:0 var(--space-1) var(--space-4) var(--space-1); text-wrap:pretty; }

  /* deadline that no longer fits the days available */
/* A notice is content (D130): no fill, a hairline on the leading edge in the
   attention colour, which is never red, and ink that reads. The text was set
   in --line, a hairline colour, and could barely be read. */
.riskbox{ border-left:var(--band-w) solid var(--danger); color:var(--ink);
    padding:var(--space-1) 0 var(--space-1) var(--space-4); font-size:var(--fs-3);
    line-height:1.55; margin:var(--space-1) 0 var(--space-4); }
.riskbox p{ margin:0; }
.riskbox p + p{ margin-top:var(--space-3); }
.riskbox b{ color:var(--ink); font-weight:var(--fw-hi); }

  /* platform / lease mode strip */
  /* Demoted out of the first slot. It is a setup suggestion, not news, and
     it was appearing above the content on every screen. */
.mode-strip{ display:flex; align-items:center; gap:var(--space-2); flex-wrap:wrap;
    order:99; margin-top:var(--space-5);
    background:color-mix(in oklab, var(--tide) 24%, transparent); border:1px solid color-mix(in oklab, var(--tide) 40%, transparent);
    color:var(--tide); border-radius:var(--r-control); padding:var(--space-3) var(--space-3); font-size:var(--fs-2);
    font-weight:var(--fw-med); line-height:1.45; margin:var(--space-1) 0 var(--space-4); }
.mode-strip button{ color:var(--tide); font-weight:var(--fw-hi); text-decoration:underline; font-size:var(--fs-2); }
.mode-strip.locked button{ color:var(--ink-soft); }
.mode-strip.locked{ background:var(--surface-2); border-color:var(--line); color:var(--ink-soft); }
.syncdot.locked{ background:var(--surface-2); }

/* Who is signed in, under the Settings title: a line of text and the one
   control that goes to Account, on the ground with no box. */
.set-who{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--space-3);
  margin:calc(var(--space-2) * -1) 0 var(--space-4); color:var(--ink-soft); font-size:var(--fs-2); }
.set-who span{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* The walkthrough's chapters, in the offer: the settings menu's rows, with
   the next one to watch named first. */
.tourch .setmenu-row.is-next b{ color:var(--ink); }

  /* ---- display face, shared with the landing page ---- */
  /* The display serif, shared. Weight 400 is right at --fs-6 where the size
     alone carries it; at --fs-5 it reads as body copy set large, so the
     smaller headings step up to 500. */
.greeting, .empty h2, .onboard h2{
    font-family:var(--font-ui);
    font-weight:var(--fw-body); letter-spacing:-0.012em;
  }
.empty h2{ font-weight:var(--fw-med); }
.greeting{ font-size:var(--fs-6); line-height:1.1; margin-bottom:var(--space-5); text-wrap:balance; }
.empty h2{ font-size:var(--fs-5); }
.onboard h2{ font-size:var(--fs-6); line-height:1.14; }
  .whoami .wi-name{ font-size:var(--fs-5); }

  /* ---- settings, rebuilt ---- */
.set-group{ font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.09em; text-transform:uppercase;
    color:var(--ink-mute); margin:var(--space-5) var(--space-1) var(--space-3); }
.set-group:first-of-type{ margin-top:var(--space-1); }
.fnote{ font-size:var(--fs-2); color:var(--ink-mute); line-height:1.5; margin-top:var(--space-2); }
.acct-row{ display:flex; align-items:center; gap:var(--space-3); }
.acct-av{ width:44px; height:44px; border-radius:50%; flex-shrink:0; display:grid; place-items:center;
    background:var(--surface-2); color:var(--accent-ink);
    font-size:var(--fs-5); font-weight:var(--fw-hi); }
.acct-nm{ font-size:var(--fs-4); font-weight:var(--fw-hi); letter-spacing:-0.01em; }
.acct-sub{ font-size:var(--fs-2); color:var(--ink-mute); margin-top:var(--space-1); }

  /* ---- hold to stop ---- */
.holdbtn{ position:relative; overflow:hidden; border-radius:var(--r-control); padding:var(--space-4);
    background:var(--ground); border:1px solid var(--line); width:100%;
    touch-action:none; -webkit-user-select:none; user-select:none; }
.hold-fill{ position:absolute; inset:0; width:var(--hold,0%);
    background:var(--surface-2);
    transition:width .06s linear; pointer-events:none; }
.hold-txt{ position:relative; display:inline-flex; align-items:center; gap:var(--space-2);
    font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink-soft); }
.holdbtn.holding .hold-txt{ color:var(--ink); }
.holdbtn.holding{ border-color:var(--surface-2); }
.hold-left{ font-variant-numeric:tabular-nums; font-weight:var(--fw-hi); color:var(--line); min-width:2ch; }

  /* ---- a session running on another device ---- */
.live-strip{ display:flex; align-items:center; gap:var(--space-3); flex-wrap:wrap;
    background:color-mix(in oklab, var(--tide) 24%, transparent); border:1px solid color-mix(in oklab, var(--tide) 40%, transparent);
    color:var(--tide); border-radius:var(--r-control); padding:var(--space-3) var(--space-4); font-size:var(--fs-2);
    font-weight:var(--fw-med); line-height:1.5; margin:var(--space-1) 0 var(--space-4); }
.live-strip div{ flex:1; min-width:180px; }
.live-strip b{ color:var(--ink); font-weight:var(--fw-hi); }
.live-strip button{ background:var(--tide); color:var(--ground); font-weight:var(--fw-hi);
    font-size:var(--fs-2); padding:var(--space-2) var(--space-4); border-radius:var(--r-pill); white-space:nowrap; }
.live-strip button:active{ transform:scale(.96); }

  /* an owed break, takeable at any point in the session */
.sess-break{ display:flex; align-items:center; gap:var(--space-2); margin:var(--space-3) auto 0;
    padding:var(--space-2) var(--space-4); border-radius:var(--r-pill); background:color-mix(in oklab, var(--tide) 28%, transparent);
    border:1px solid color-mix(in oklab, var(--tide) 40%, transparent); color:var(--tide);
    font-size:var(--fs-2); font-weight:var(--fw-hi); }
.sess-break:active{ transform:scale(.96); background:color-mix(in oklab, var(--tide) 40%, transparent); }

  /* ---- where you are in today ---- */
.today-strip{ margin:0 0 var(--space-4); }
.ts-top{ display:flex; justify-content:space-between; font-size:var(--fs-2);
    color:var(--ink-mute); font-weight:var(--fw-med); margin-bottom:var(--space-2); font-variant-numeric:tabular-nums; }
.ts-top b{ color:var(--ink); font-weight:var(--fw-hi); }
  /* A bar you can read.
     The track was --surface, which resolves to --ground, and the fill was
     --surface-2, which is 0.025 of lightness away from it. Two hundred and
     forty minutes of work and no work at all looked identical. The track is
     the hairline and the fill is ink: no colour is spent, and both ends of
     the range are legible. */
.ts-bar{ height:6px; border-radius:var(--r-pill); background:var(--line-soft);
    overflow:hidden; }
.ts-bar i{ display:block; height:100%; border-radius:var(--r-pill);
    background:var(--ink-soft); transition:width var(--dur-base) var(--ease); }
.ts-sub{ font-size:var(--fs-1); color:var(--ink-mute); margin-top:var(--space-2); }

  /* ---- the day ring, sitting inside the session ring ---- */
.ring-bg.day{ stroke:var(--surface); stroke-width:5; }
.ring-day{ fill:none; stroke:var(--tide); stroke-width:5; stroke-linecap:round;
    opacity:.75; transition:stroke-dashoffset var(--dur-base) var(--ease); }

  /* ---- the reward moment ---- */
.reward-card .reward-ring{ position:relative; width:96px; height:96px; margin:0 auto var(--space-4); }
.reward-ring svg{ width:96px; height:96px; transform:rotate(-90deg); }
.reward-ring circle{ fill:none; stroke:var(--surface-2); stroke-width:5; }
.reward-ring .rr-fg{ stroke:var(--line); stroke-linecap:round;
    stroke-dasharray:264; stroke-dashoffset:264; animation:rrSweep var(--dur-base) var(--ease) forwards; }
@keyframes rrSweep{to{ stroke-dashoffset:0; } }
.reward-ring .coin-dot{ position:absolute; left:50%; top:50%; width:40px; height:40px;
    transform:translate(-50%,-50%);
    animation:coinPop var(--dur-base) var(--ease) both; }
@keyframes coinPop{from{ transform:translate(-50%,-50%) scale(.4); opacity:0; }
to{ transform:translate(-50%,-50%) scale(1); opacity:1; } }
.reward-glow.big{ background:var(--surface-2); }
.reward .reward-card{ animation:rwIn var(--dur-base) var(--ease) both; }
@keyframes rwIn{from{ opacity:0; transform:scale(.86); }to{ opacity:1; transform:scale(1); } }
.reward.out .reward-card{ animation:rwOut var(--dur-base) var(--ease) forwards; }
@keyframes rwOut{to{ opacity:0; transform:scale(.96) translateY(-8px); } }
.reward-amt{ font-variant-numeric:tabular-nums; }

  /* ---- a task that keeps sliding ---- */
.stuck-tag{ color:var(--line); font-weight:var(--fw-hi); }

  /* ---- add to home screen ---- */
.inst-steps{ display:flex; flex-direction:column; gap:var(--space-3); margin:var(--space-1) 0 var(--space-4); text-align:left; }
.inst-step{ display:flex; align-items:flex-start; gap:var(--space-3); }
.inst-ic{ width:38px; height:38px; border-radius:var(--r-control); flex-shrink:0; display:grid; place-items:center;
    background:var(--surface-2); color:var(--ink-soft); }
.inst-ic svg{ width:19px; height:19px; }
.inst-step b{ display:block; font-size:var(--fs-3); font-weight:var(--fw-hi); margin-bottom:var(--space-1); }
.inst-step span:last-child{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.45; }
.inst-done{ font-size:var(--fs-3); color:var(--tide); font-weight:var(--fw-med); margin:var(--space-1) 0 var(--space-4); }

  /* ---- a day you can open ---- */
.pd-head{ display:flex; align-items:center; justify-content:space-between;
    margin-bottom:var(--space-2); cursor:pointer; user-select:none; }
.pd-right{ display:flex; align-items:center; gap:var(--space-2); }
.pd-chev{ width:16px; height:16px; color:var(--ink-mute); transition:transform var(--dur-base) var(--ease); }
.pday.open .pd-chev{ transform:rotate(180deg); }
.pday:not(.open) .pd-list{ display:none; }
.pday:not(.open) .pd-bar{ margin-bottom:0; }
.pd-task{ position:relative; }
.pd-del{ width:26px; height:26px; border-radius:var(--r-control); flex-shrink:0; display:grid; place-items:center;
    color:var(--ink-mute); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.pd-del svg{ width:14px; height:14px; }
.pd-del:active{ background:var(--surface-2); color:var(--danger); }

  /* ---- goals: outcomes, milestones, and an honest capacity read ---- */
.goal-empty{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-card);
    padding:var(--space-5) var(--space-5); text-align:center; margin-bottom:var(--space-2); }
.goal-empty p{ font-size:var(--fs-3); color:var(--ink-soft); line-height:1.6; margin-bottom:var(--space-4);
    max-width:38ch; margin-inline:auto; }

.cap{ margin:var(--space-1) 0 var(--space-4); }
.cap-top{ display:flex; justify-content:space-between; font-size:var(--fs-2); color:var(--ink-mute);
    font-weight:var(--fw-med); margin-bottom:var(--space-2); font-variant-numeric:tabular-nums; }
.cap-bar{ height:7px; border-radius:var(--r-pill); background:var(--surface);
    border:1px solid var(--line); overflow:hidden; }
.cap-bar i{ display:block; height:100%; border-radius:var(--r-pill);
    background:var(--tide); transition:width var(--dur-base) var(--ease); }
.cap-bar.tight i{ background:var(--surface-2); }
.cap-bar.over i{ background:var(--danger); }
.cap-note{ font-size:var(--fs-2); color:var(--ink-mute); line-height:1.5; margin-top:var(--space-2); }
.cap-note.over{ color:var(--line); }

.goals{ display:flex; flex-direction:column; gap:var(--space-3); }
.goal{ background:var(--surface); border:1px solid var(--line); border-radius:var(--r-card);
    padding:var(--space-4); animation:rise var(--dur-base) var(--ease) both; }
.goal-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:var(--space-3); margin-bottom:var(--space-3); }
.goal-name{ font-size:var(--fs-4); font-weight:var(--fw-hi); letter-spacing:-0.02em; line-height:1.25; }
.goal-sub{ font-size:var(--fs-2); color:var(--ink-mute); margin-top:var(--space-1); font-weight:var(--fw-med); }
.goal-x{ width:28px; height:28px; border-radius:var(--r-control); flex-shrink:0; display:grid; place-items:center;
    color:var(--ink-mute); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.goal-x svg{ width:14px; height:14px; }
.goal-x:active{ background:var(--surface); color:var(--ink-soft); }
.goal-why{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.55; margin:var(--space-3) 0 var(--space-1); }

.miles-list{ display:flex; flex-direction:column; gap:var(--space-1); margin-top:var(--space-3); }
.mile-row{ display:flex; align-items:center; gap:var(--space-3); width:100%; text-align:left;
    padding:var(--space-2) var(--space-1); border-radius:var(--r-control); transition:background var(--dur-fast) var(--ease); }
.mile-row:active{ background:var(--surface); }
.mile-box{ width:17px; height:17px; border-radius:var(--r-control); flex-shrink:0; display:grid; place-items:center;
    border:1.8px solid var(--ink-mute); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.mile-box svg{ width:10px; height:10px; color:var(--accent-ink); }
.mile-row.done .mile-box{ background:var(--surface-2); border-color:var(--line); }
.mile-nm{ flex:1; font-size:var(--fs-3); font-weight:var(--fw-med); color:var(--ink-soft); min-width:0; }
.mile-row.done .mile-nm{ color:var(--ink-mute); text-decoration:line-through;
    text-decoration-color:var(--ink-mute); }
.mile-due{ font-size:var(--fs-1); color:var(--ink-mute); flex-shrink:0; font-variant-numeric:tabular-nums; }

  /* ---- today's fixed commitments ---- */
.classes{ display:flex; flex-direction:column; gap:var(--space-1); margin:0 0 var(--space-4); }
.cls{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2) var(--space-3); border-radius:var(--r-control);
    background:var(--surface); border:1px solid var(--line); }
.cls-t{ font-size:var(--fs-1); color:var(--ink-mute); font-variant-numeric:tabular-nums;
    font-weight:var(--fw-hi); flex-shrink:0; width:42px; }
.cls-n{ font-size:var(--fs-3); font-weight:var(--fw-med); color:var(--ink-soft); min-width:0;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.cls.now{ border-color:color-mix(in oklab, var(--tide) 40%, transparent); background:color-mix(in oklab, var(--tide) 2%, transparent); }
.cls.now .cls-t, .cls.now .cls-n{ color:var(--tide); }
.cls.past{ opacity:.45; }

  /* ---- the week editor ---- */
.wk-list{ display:flex; flex-direction:column; gap:var(--space-4); margin-bottom:var(--space-1); }
.wk-name{ font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.07em; text-transform:uppercase;
    color:var(--ink-mute); margin-bottom:var(--space-2); }
.wk-row{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2) 0;
    border-bottom:1px solid var(--line); }
.wk-row:last-child{ border-bottom:none; }
.wk-time{ font-size:var(--fs-2); color:var(--ink-mute); font-variant-numeric:tabular-nums;
    flex-shrink:0; width:92px; }
.wk-cls{ flex:1; font-size:var(--fs-3); font-weight:var(--fw-med); min-width:0;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:left; padding:0; }

  /* ---- things pinned to a clock time ---- */
.pd-clock{ font-size:var(--fs-1); font-weight:var(--fw-hi); color:var(--ink-soft); flex-shrink:0;
    min-width:44px; white-space:nowrap; font-variant-numeric:tabular-nums; letter-spacing:-0.01em; }
.pd-task.timed .pd-nm{ color:var(--ink); font-weight:var(--fw-hi); }
.pd-fixed{ display:flex; flex-direction:column; gap:var(--space-1); margin-bottom:var(--space-2);
    padding-bottom:var(--space-2); border-bottom:1px dashed var(--line); }
.pd-fix{ display:flex; align-items:center; gap:var(--space-2); }
.pd-fix .pd-clock{ color:var(--tide); }
.pd-fixnm{ flex:1; font-size:var(--fs-2); font-weight:var(--fw-med); color:var(--ink-soft);
    min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:left; padding:0; }
/* A name on a row that opens what it names: text, not a control's box. */
.rowlink{ text-align:left; padding:0; }
/* A meeting with a joining link, D117: the name is in the accent, the colour
   the founder asked for, because it is the one name on the day that opens
   something outside Sailyn. The accent is a field everywhere else. */
.pd-fixnm.has-link, .wk-cls.has-link, .pd-nm.has-link,
.prow-open.has-link .prow-nm, .nowrow-go.has-link .nowrow-title{ color:var(--accent); }
/* Time kept free: a held stretch, said quietly, never a thing to start. */
.pd-fix.is-kept .pd-clock, .pd-fix.is-kept .pd-fixnm{ color:var(--ink-mute); font-weight:var(--fw-body); }
/* Said done (plan/fixed.js fixedIsDone): greyed and labelled, never struck through. */
.pd-fix.is-over .pd-clock, .pd-fix.is-over .pd-fixnm{ color:var(--ink-mute); }
.pd-pin.is-done{ display:inline-flex; align-items:center; gap:var(--space-1); }
.pd-pin.is-done .pbox{ width:14px; height:14px; }
.pd-pin.is-done .pbox svg{ width:9px; height:9px; }
.timefield .block.is-done .block-n{ color:var(--ink-mute); }
.donetoday{ margin-bottom:var(--space-3); }
.pd-pin{ font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.06em; text-transform:uppercase;
    color:var(--ink-mute); flex-shrink:0; }

  /* ---- exam readiness ---- */
.ready{ display:flex; flex-direction:column; gap:var(--space-3); padding:var(--space-5);
    background:var(--surface); border-top:1px solid var(--line);
    border-radius:var(--r-card); margin:var(--space-1) 0 var(--space-5); }
.ready-body{ min-width:0; }
.ready-t{ font-size:var(--fs-4); font-weight:var(--fw-hi); letter-spacing:-0.02em; margin-bottom:var(--space-1); }
/* A deadline's name opens its task: a control, so it keeps the look of the
   heading it replaces and shows it can be pressed by the pointer and a
   hairline under it on hover and focus. */
.ready-open{ display:block; width:100%; text-align:left; background:none; border:0; padding:0; color:inherit;
  font-family:inherit; cursor:pointer; text-decoration:underline; text-decoration-color:var(--line);
  text-underline-offset:4px; }
.ready-open:hover, .ready-open:focus-visible{ text-decoration-color:currentColor; }
.ready-tasks{ display:flex; flex-direction:column; gap:var(--space-2); margin-top:var(--space-3); }
.ready-task{ font-size:var(--fs-2); color:var(--ink-soft); }
.ready-d{ font-size:var(--fs-2); color:var(--ink-mute); font-weight:var(--fw-med); margin-bottom:var(--space-3); }
.ready-bar{ position:relative; height:6px; border-radius:var(--r-pill); background:var(--surface-2); overflow:hidden; margin-bottom:var(--space-2); }
.ready-bar i{ display:block; height:100%; border-radius:var(--r-pill); background:var(--tide); transition:width var(--dur-base) var(--ease); }
.ready.good .ready-bar i{ background:var(--tide); }
.ready.mid .ready-bar i{ background:var(--line-strong); }
.ready.risk .ready-bar i{ background:var(--danger); }
.ready-pct{ position:absolute; right:0; top:-20px; font-family:var(--font-num); font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-mute); font-variant-numeric:tabular-nums; }
.ready-n{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.5; }
.ready.risk .ready-n{ color:var(--danger); }

  /* ---- how much today is carrying ---- */
.load{ margin:0 0 var(--space-4); padding:var(--space-4) var(--space-4); border-radius:var(--r-card);
    background:var(--surface); border:1px solid var(--line); }
.load-top{ display:flex; align-items:baseline; justify-content:space-between; margin-bottom:var(--space-2); }
.load-lbl{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-mute); }
.load-val{ font-size:var(--fs-5); font-weight:var(--fw-hi); letter-spacing:-0.03em; font-variant-numeric:tabular-nums; }
.load-bar{ height:6px; border-radius:var(--r-pill); background:var(--line-soft); overflow:hidden; }
.load-bar i{ display:block; height:100%; border-radius:var(--r-pill);
    background:var(--tide); transition:width var(--dur-base) var(--ease); }
.load.full .load-bar i{ background:var(--ink-soft); }
.load.over .load-bar i{ background:var(--danger); }
.load.over .load-val{ color:var(--line); }
.load-note{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.5; margin-top:var(--space-3); }
.load.over .load-note{ color:var(--line); }
.load-meta{ font-size:var(--fs-1); color:var(--ink-mute); margin-top:var(--space-1); }

  /* ---- friction ---- */
.fric-tag{ color:var(--line); font-weight:var(--fw-hi); }
.ease{ display:flex; align-items:center; justify-content:center; gap:var(--space-2); width:100%;
    margin:0 0 var(--space-3); padding:var(--space-3); border-radius:var(--r-control); font-size:var(--fs-2); font-weight:var(--fw-hi);
    background:var(--surface-2); border:1px solid var(--surface-2);
    color:var(--ink-soft); }
.ease svg{ width:15px; height:15px; }
.ease:active{ transform:scale(.98); }

  /* ---- the week ---- */
.review{ background:var(--surface); border:1px solid var(--line);
    border-radius:var(--r-card); padding:var(--space-4); margin-bottom:var(--space-5); }
.rv-spark{ display:flex; align-items:flex-end; gap:var(--space-1); height:64px; margin-bottom:var(--space-4); }
.rv-col{ flex:1; display:flex; flex-direction:column; align-items:center;
    justify-content:flex-end; height:100%; gap:var(--space-1); }
.rv-fill{ width:100%; min-height:3px; border-radius:var(--r-control);
    background:var(--surface-2); opacity:.85; }
.rv-col span{ font-size:var(--fs-1); color:var(--ink-mute); font-weight:var(--fw-hi); }
.rv-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:var(--space-3) var(--space-4);
    padding-bottom:var(--space-4); border-bottom:1px solid var(--line); }
.rv-grid div{ display:flex; flex-direction:column; gap:var(--space-1); }
.rv-grid b{ font-size:var(--fs-5); font-weight:var(--fw-hi); letter-spacing:-0.03em; font-variant-numeric:tabular-nums; }
.rv-grid span{ font-size:var(--fs-1); color:var(--ink-mute); line-height:1.35; }
.rv-line{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.6; margin-top:var(--space-3); }
.rv-line b{ color:var(--ink); font-weight:var(--fw-hi); }

  /* ---- dictation ---- */
.plantext-wrap{ position:relative; }
.plantext-wrap .plantext{ padding-right:var(--space-7); }
.mic{ position:absolute; right:10px; bottom:10px; width:34px; height:34px; border-radius:var(--r-control);
    display:grid; place-items:center; color:var(--ink-mute);
    background:var(--surface); border:1px solid var(--line); transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease), background-color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
.mic svg{ width:16px; height:16px; }
.mic.hot{ color:var(--accent-ink); background:var(--surface-2); border-color:var(--line);
    animation:pulse 1.3s ease-in-out infinite; }

  /* ---- starting points instead of a blank box ---- */
.convos{ display:flex; flex-wrap:wrap; gap:var(--space-2); margin-bottom:var(--space-3); }
.convos button{ font-size:var(--fs-2); font-weight:var(--fw-med); padding:var(--space-2) var(--space-3); border-radius:var(--r-pill);
    background:color-mix(in oklab, var(--tide) 24%, transparent); border:1px solid color-mix(in oklab, var(--tide) 6%, transparent);
    color:var(--tide); }
.convos button:active{ transform:scale(.96); }

  /* ---- what the week is asking of you ---- */
.mode-hint{ font-size:var(--fs-2); color:var(--ink-mute); line-height:1.5;
    padding:0 0 0 var(--space-4); border-radius:0; background:none;
    border:none; border-left:var(--band-w) solid var(--line);
    margin-bottom:var(--space-5); max-width:70ch; }
.mode-hint.exam{ color:var(--ink-soft); border-left-color:var(--line-strong); background:none; }
  /* Machine state, said with a rule rather than a tint. A filled strip across
     the top of the screen is the weight of a warning, and "quiet week" is an
     observation. */
.mode-hint.open{ color:var(--ink-soft); border-left-color:var(--tide); background:none; }

  /* ---- the season ---- */
.season{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.5; padding:var(--space-3) var(--space-3);
    border-radius:var(--r-control); background:var(--surface-2);
    border:1px solid var(--surface-2); margin-bottom:var(--space-4); }
.season b{ color:var(--ink-soft); font-weight:var(--fw-hi); }

/* A day drawn to real time, with drag (18 October, plan/dayview.js). The
   field is the day field's own; a block you can move takes a grip at its
   foot for its length and follows the pointer without scrolling the page. */
.dvsheet .sheet-body{ padding-bottom: var(--space-6); }
.dv-days{ margin-bottom: var(--space-2); }
.dayview{ margin-top: var(--space-3); }
.timefield .block.is-drag{ cursor: grab; touch-action: none; user-select: none; }
.timefield .block.is-drag:active{ cursor: grabbing; }
.timefield .block.is-drag:focus-visible{ outline: none; box-shadow: var(--focus-ring); }
.dv-grip{ position: absolute; left: 0; right: 0; bottom: 0; height: 8px; cursor: ns-resize; touch-action: none; border-bottom: 2px solid var(--line-strong); }
.dv-unplaced{ margin-top: var(--space-3); }
.dayopen{ margin-left: auto; }
.nowedge{ display: flex; align-items: center; gap: var(--space-2); margin: var(--space-1) 0 var(--space-3); font-size: var(--fs-2); color: var(--ink-soft); }
.nowedge .ic{ flex: 0 0 auto; }

  /* =====================================================================
     Material. Glass is a layer at a real elevation, not a blur effect.
     Each level is further from the page and catches slightly more light,
     which is what makes the hierarchy readable without borders.
     ===================================================================== */
  /* =====================================================================
     Material, and where it belongs.

     Glass is the navigation and temporary layer: the bar, the sheets, the
     things that sit above what you are reading. It was on nine content
     elements too, which is the one thing the guidance is emphatic about not
     doing, because a material meant to draw attention to content underneath
     cannot do that when the content is made of it as well.

     Content keeps its elevation, since that is hierarchy rather than
     decoration. It just stops being see through.
     ===================================================================== */

  /* Refraction was attempted here and removed with the glass material.
     Surfaces separate by value now. */

.glass, .onboard{ position:relative; }
.glass, .sheet, .onboard{
    background:var(--surface);
    border:1px solid transparent;
    box-shadow:var(--shadow-3);
    transition:background var(--dur-base) var(--ease);
  }
.sheet, .onboard{ box-shadow:var(--shadow-4); }
  

  /* The highlight. It travels across the surface as the surface moves through
     the world, which is the difference between glass and a blurred rectangle. */
  

  /* Thickness. Over nothing the surface is nearly clear; once content is
     passing behind it, it thickens enough to stay legible. Driven from script
     rather than guessed, because only the page knows what is under there. */
  /* Solid. This was the last of the glass: a color-mix that left the tab bar
     30% transparent, so content scrolled visibly through it and the bar read
     as half-there. A navigation bar is either present or it is not. */
  /* Scoped to the phone. Unscoped it also won on desktop, where the rail is
     meant to be --surface-2, the one genuinely different plane in this system.
     The rail was therefore pure white, the page beside it was near-white, and
     the only thing distinguishing the two was the border between them. */
@media (max-width: 879px){
#app nav{
    background:var(--surface-solid);
    transition:background var(--dur-base) var(--ease);
  }
  }

  /* The card soup, and where it actually lived.
   *
   * One rule put `border:1px solid var(--line)` on all four sides of every
   * task row, goal, readiness panel, insight, class and empty state in the
   * product. --surface resolves to --ground so the fill was invisible and only
   * the outline showed, which is the exact thing the world layer exists to
   * refuse: "never four edges around a region". Nine component families were
   * drawn as boxes by a rule none of them declared, several hundred lines away
   * from any of them, under a comment describing a look this system replaced.
   *
   * Separation is a hairline on one edge. Each of these families carries its
   * own, so removing this leaves them separated and stops them floating. */


  /* Kept only on the sheets, where the material is real. On a solid card it
     was a gradient pretending to be a reflection. */


  /* buttons compress rather than bounce, and light moves across them */

  /* navigation floats free of the page -- mobile only. Unscoped, this used to
     win the cascade on desktop too (same #app nav / #app header selectors as
     the >=880px rail), stomping the rail's top padding and solid background
     and causing the fixed header to overlap the first nav items. */
@media (max-width: 879px){
    /* A bar, not an island. This rule used to round the nav to a 999px pill
       and float it with margins, which overrode the prompt-2 fix 700 lines
       above it purely by coming later. flex-direction is restated because a
       leaked desktop rule would otherwise stack the tabs vertically, and this
       is the rule that has to win on a phone. */
#app nav{
      position:relative;
      z-index:var(--z-nav);
      flex-direction:row;
      width:auto; order:0;
      background:var(--surface-solid);
      border:none; border-top:1px solid var(--line);
      border-radius:0;
      margin:0;
      /* Tightened when the sixth tab arrived. Six labels across a 375px phone
       leaves 54px each, and the horizontal padding was the cheapest 32px to
       give back. */
    padding:var(--space-2) var(--space-2) max(var(--space-2), var(--safe-bottom));
    }
#app nav button{ border-radius:var(--r-control); transition:background var(--dur-base) var(--ease), color var(--dur-base) var(--ease); }
    /* Said two ways on a phone: the label fills in and the icon takes the
       accent. There is no room for the rail's edge rule down here. */
#app nav button.on{ color:var(--ink); font-weight:var(--fw-hi); background:var(--surface-2); }
#app nav button.on svg{ color:var(--accent); }

#app header{
      background:var(--ground);
    }
  }
.coins{ background:var(--surface-2); border:1px solid var(--line); color:var(--ink-soft); }
.coin-dot{ background:var(--gold); }

  /* nothing pops in: it settles */
@keyframes settle{
from{ opacity:0; transform:translateY(14px) scale(.985); }
to{ opacity:1; transform:none; }
  }
  /* The stagger follows the content into the page shell. Without this only
     .page itself animated, so a whole screen faded in as one slab. */
#app main > *, .page-main > *, .page-side > *{ animation:settle var(--dur-base) var(--ease) both; }
#app main > *:nth-child(2), .page-main > *:nth-child(2){ animation-delay:.04s; }
#app main > *:nth-child(3), .page-main > *:nth-child(3){ animation-delay:.08s; }
#app main > *:nth-child(4), .page-main > *:nth-child(4){ animation-delay:.12s; }
.page{ animation:none; }
@media (prefers-reduced-motion:reduce){
#app main > *{ animation:none; }
  }

  /* ---- when your day starts and ends ---- */
.daybtn{ display:flex; align-items:center; gap:var(--space-3); width:100%; padding:var(--space-3) var(--space-4);
    border-radius:var(--r-control); margin-bottom:var(--space-4); text-align:left;
    background:var(--surface); border:1px solid var(--line);
    font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink);
    transition:transform var(--dur-base) var(--ease); }
.daybtn svg{ width:19px; height:19px; color:var(--ink-soft); flex-shrink:0; }
.daybtn.up svg{ color:var(--ink-soft); }
.daybtn-sub{ margin-left:auto; font-size:var(--fs-2); font-weight:var(--fw-med); color:var(--ink-mute); }
.daybtn:active{ transform:scale(.99); }

  /* ---- where you live ---- */
.home{ background:var(--surface); border:1px solid var(--line);
    border-radius:var(--r-card); padding:var(--space-4); margin:var(--space-4) 0 var(--space-5); }
.home-top{ display:flex; align-items:flex-start; justify-content:space-between; gap:var(--space-3); margin-bottom:var(--space-3); }
.home-name{ font-size:var(--fs-4); font-weight:var(--fw-hi); letter-spacing:-0.02em; }
.home-sub{ font-size:var(--fs-2); color:var(--ink-mute); margin-top:var(--space-1); }
.home-step{ font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.06em; color:var(--ink-soft);
    background:var(--surface-2); padding:var(--space-1) var(--space-2); border-radius:var(--r-pill); white-space:nowrap; }
.home-note{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.55; margin-top:var(--space-3); }
.home-note b{ color:var(--ink-soft); font-weight:var(--fw-hi); }
.shop-next{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.6; margin-top:var(--space-3);
    padding:var(--space-3) var(--space-4); border-radius:var(--r-control);
    background:var(--surface-2); border:1px solid var(--line); }
.shop-next b{ color:var(--ink); font-weight:var(--fw-hi); }
.shop-later{ font-size:var(--fs-1); color:var(--ink-mute); margin-top:var(--space-2); }

  /* ---- sites the extension holds shut ---- */
.sites{ display:flex; flex-wrap:wrap; gap:var(--space-2); margin-top:var(--space-3); }
.site-chip{ display:inline-flex; align-items:center; gap:var(--space-2); font-size:var(--fs-2); font-weight:var(--fw-med);
    padding:var(--space-2) var(--space-2) var(--space-2) var(--space-3); border-radius:var(--r-pill); background:var(--surface);
    border:1px solid var(--line); color:var(--ink); }
  /* Nineteen pixels is a target you miss. The circle stays small because the
     chip is small, but the area that answers a finger does not have to match
     what is drawn. */
.site-chip button{ width:19px; height:19px; border-radius:50%; display:grid; place-items:center;
    color:var(--ink-mute); background:none; flex-shrink:0; position:relative; }
.site-chip button::after{
    content:""; position:absolute; inset:-11px; border-radius:50%;
  }
.site-chip button svg{ width:10px; height:10px; }
.site-chip button:active{ background:var(--surface-2); color:var(--danger); }
.site-sugg{ display:flex; flex-wrap:wrap; gap:var(--space-1); margin-top:var(--space-2); }
.site-sugg button{ font-size:var(--fs-1); font-weight:var(--fw-med); padding:var(--space-1) var(--space-3); border-radius:var(--r-pill);
    background:none; border:1px dashed var(--line); color:var(--ink-mute); }
.site-sugg button:active{ border-style:solid; color:var(--ink-soft); border-color:var(--line); }

  /* ---- rows the markup asks for that were never styled ----
     Each of these was used in a template while its rule either never existed
     or was removed as unused before the markup caught up. */
  

.skill{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-3) 0; }
.skill + .skill{ border-top:1px solid var(--line); }
.skill .sk-nm{ font-size:var(--fs-3); font-weight:var(--fw-med); min-width:84px; flex-shrink:0; }
.skill .bar{ flex:1; }
  .skill .sk-lv{ font-size:var(--fs-2); color:var(--ink-mute); font-variant-numeric:tabular-nums;
    flex-shrink:0; min-width:34px; text-align:right; }

.score{ display:flex; flex-direction:column; gap:var(--space-2); padding:var(--space-3) 0; }
.score + .score{ border-top:1px solid var(--line); }
.score .sc-top{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--space-2); }
  .score .sc-nm{ font-size:var(--fs-3); font-weight:var(--fw-hi); }
.score .sc-lbl{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-soft); }
.score .bar{ height:8px; margin-top:var(--space-1); }
.score .sc-top{ align-items:flex-end; }

  /* The number is the answer, and it was set two points larger than the word
     beside it. That made the label almost as loud as the thing it labels. */
.score .sc-val{ font-size:var(--fs-6); font-weight:var(--fw-hi); letter-spacing:-0.035em;
    line-height:1; color:var(--ink);
    font-variant-numeric:tabular-nums; }
  .score .sc-why{ font-size:var(--fs-2); color:var(--ink-soft); line-height:1.55; }

.wk-day{ margin-bottom:var(--space-1); }
.wk-day:last-child{ margin-bottom:0; }

.authform{ margin:var(--space-3) 0 var(--space-1); }
.authform input{ width:100%; padding:var(--space-3) var(--space-4); border-radius:var(--r-control);
    background:var(--surface); border:1px solid var(--line); color:var(--ink);
    font-size:var(--fs-4); -webkit-appearance:none; }
.authform input:focus-visible{ outline:none; border-color:var(--accent); }
.authform input::placeholder{ color:var(--ink-mute); }

  /* a link styled as a segmented button, for downloads */
.segbtn{ display:inline-flex; align-items:center; justify-content:center; flex:1;
    padding:var(--space-3) var(--space-3); border-radius:var(--r-control); font-size:var(--fs-3); font-weight:var(--fw-hi);
    background:var(--surface); border:1px solid var(--line); color:var(--ink);
    text-decoration:none; transition:transform var(--dur-base) var(--ease); }
.segbtn:active{ transform:scale(.98); }
.opt.ok{ color:var(--tide); font-weight:var(--fw-hi); }

  /* ---- offline ----
     A state, not an error. It says what still works before it says what does
     not, because almost everything still works. */
.offbar{ display:flex; align-items:center; gap:var(--space-2); font-size:var(--fs-2); line-height:1.5;
    padding:var(--space-2) var(--space-3); border-radius:var(--r-control); margin-bottom:var(--space-4);
    color:var(--danger); background:var(--danger);
    border:1px solid var(--danger); }

.offbar i{ width:8px; height:8px; border-radius:50%; flex-shrink:0;
    background:var(--line); animation:pulse 2.4s ease-in-out infinite; }
@media (prefers-reduced-motion:reduce){.offbar i{ animation:none; } }

.dimmed{ opacity:.45; filter:saturate(.4); }

.ob-fine{ font-size:var(--fs-1); color:var(--ink-mute); line-height:1.55;
    margin-top:var(--space-3); text-align:center; }

  /* =====================================================================
     Accessibility fallbacks the material requires.

     Glass is a preference, not a requirement. Three system settings change
     what it should be, and a translucent surface that ignores them is a
     legibility problem rather than a style choice.
     ===================================================================== */

  /* Reduce Transparency: the material becomes frosted, which on the web means
     opaque. Nothing shows through, and nothing moves position. */
@media (prefers-reduced-transparency: reduce){
.glass, .sheet, .onboard, #app nav, #app header{
      background:var(--surface);
    }
.sheet::before, .onboard::before{ display:none; }
.offbar, .season, .shop-next, .mode-hint{
      background:var(--surface);
    }
  }

  /* Increase Contrast: borders become definite rather than suggested, and the
     softest text steps up to the next level. */
@media (prefers-contrast: more){
.glass, .sheet, .onboard, #app nav{ border-width:1.5px; }
    :root, 
    .glass, .sheet, .onboard, #app nav,
    #app main .tcard, #app main .review, #app main .goal, #app main .ready,
#app main .load, #app main .insight, #app main .empty, #app main .taskrow{
      border-width:1.5px;
    }
#app nav button.on{
    color:var(--ink);
    background:none;
    /* Weight as well as colour. The rail was fixed during the regression
       reversal and this was left saying it in colour alone, which is the one
       signal that disappears for anyone who cannot see it. */
    font-weight:var(--fw-hi);
  }
  }

  /* Forced colours: hand the whole thing over to the system palette rather
     than fighting it with a translucent surface it cannot see through. */
@media (forced-colors: active){
    .glass, .sheet, .onboard, #app nav, #app header,
.syncdot, .offbar i{ forced-color-adjust:none; }
  }

  /* Where the floating bar meets scrolling content. Without this a line of
     text can sit half behind the glass and read as neither. */
#app main{ padding-bottom:calc(var(--safe-bottom) + var(--space-7)); }
#app::after{
    content:""; position:fixed; left:0; right:0; pointer-events:none;
    bottom:0; height:calc(var(--safe-bottom) + 88px); z-index:calc(var(--z-nav) - 1);
    background:var(--ground);
  }
@media (prefers-reduced-transparency: reduce){
#app::after{ background:var(--ground); height:calc(var(--safe-bottom) + 76px); }
  }
@media (min-width: 880px){
#app::after{ display:none; }        /* the rail is beside content, not over it */
#app main{ padding-bottom:var(--space-7); }
  }

  /* When motion or transparency is turned down, the surface stops moving and
     settles at its most legible value rather than its prettiest. */
@media (prefers-reduced-motion:reduce){
.glass::before, .sheet::before, .onboard::before, #app nav::before{ display:none; }
.glass, .sheet, .onboard, #app nav{ transition:none; }
  }
@media (prefers-reduced-transparency: reduce){
.glass::before, .sheet::before, .onboard::before, #app nav::before{ display:none; }
.glass::after, .sheet::after, .onboard::after, #app nav::after{ display:none; }
.glass, .sheet, .onboard, #app nav{ border:1px solid var(--line); }
  }
@media (forced-colors: active){
    .glass::before, .glass::after, .sheet::before, .sheet::after,
.onboard::before, .onboard::after, #app nav::before, #app nav::after{ display:none; }
  }

  /* ---- the sound, on the session screen ---- */
.envbar{ display:flex; align-items:center; gap:var(--space-2); align-self:center;
    padding:var(--space-2) var(--space-2) var(--space-2) var(--space-3); border-radius:var(--r-pill); margin-bottom:var(--space-4);
    background:var(--surface); border:1px solid var(--line); }
.env-wave{ display:flex; align-items:flex-end; gap:var(--space-1); height:13px; }
.env-wave i{ width:2.5px; border-radius:var(--r-pill); background:var(--tide);
    animation:envpulse 1.5s ease-in-out infinite; }
.env-wave i:nth-child(1){ height:60%; animation-delay:0s; }
.env-wave i:nth-child(2){ height:100%; animation-delay:.22s; }
.env-wave i:nth-child(3){ height:45%; animation-delay:.44s; }
@keyframes envpulse{0%, 100%{ transform:scaleY(.45); }50%{ transform:scaleY(1); } }
.env-nm{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-soft); white-space:nowrap; }
.env-skip{ width:28px; height:28px; border-radius:50%; display:grid; place-items:center;
    color:var(--ink-mute); background:none; flex-shrink:0; }
.env-skip svg{ width:14px; height:14px; }
.env-skip:active{ background:var(--surface); color:var(--ink); }
@media (prefers-reduced-motion:reduce){.env-wave i{ animation:none; height:70%; } }
.envmodes{ width:100%; max-width:280px; margin-bottom:var(--space-4); }
.envmodes button{ padding:var(--space-2) var(--space-1); font-size:var(--fs-2); }

.envrow{ display:flex; align-items:center; gap:var(--space-3); width:100%; padding:var(--space-3) var(--space-3);
    border-radius:var(--r-control); text-align:left; margin-bottom:var(--space-1);
    background:var(--surface); border:1px solid transparent; }
.envrow.on{ border-color:var(--line); background:var(--surface-2); }
.envrow-nm{ font-size:var(--fs-3); font-weight:var(--fw-hi); }
.envrow-hint{ font-size:var(--fs-1); color:var(--ink-mute); margin-top:var(--space-1); line-height:1.4; }
.envrow-tag{ margin-left:auto; font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.05em;
    text-transform:uppercase; padding:var(--space-1) var(--space-2); border-radius:var(--r-pill); flex-shrink:0; }
.envrow-tag.studied{ background:color-mix(in oklab, var(--tide) 16%, transparent); color:var(--tide); }
.envrow-tag.untested{ background:var(--surface-2); color:var(--tide); }
.envvol{ width:100%; margin-top:var(--space-1); accent-color:var(--ink-soft); }
.opt.ok{ color:var(--tide); }

  /* ---- a routine can have a time, or not ---- */
.rt-row{ display:flex; align-items:center; gap:var(--space-2); margin-top:var(--space-2); flex-wrap:wrap; }
.rt-time{ color:var(--ink); font-size:var(--fs-2); padding:var(--space-1) var(--space-2); width:auto; min-width:96px;
    border-radius:var(--r-control); border:1px solid var(--line);
    background:var(--surface); color:var(--ink); font-family:inherit; }
.rt-bell{ display:inline-flex; align-items:center; gap:var(--space-1); font-size:var(--fs-1); font-weight:var(--fw-hi);
    padding:var(--space-1) var(--space-2); border-radius:var(--r-pill); color:var(--ink-mute);
    background:var(--surface); border:1px solid var(--line); }
.rt-bell.on{ color:var(--tide); border-color:var(--line); background:var(--surface-2); }
.rt-bell:disabled{ opacity:.4; }

  /* ---- buddies ---- */
.bud{ display:flex; align-items:center; gap:var(--space-2); padding:var(--space-2) 0; }
.bud + .bud{ border-top:1px solid var(--line); }
.bud-nm{ flex:1; font-size:var(--fs-3); font-weight:var(--fw-hi); }
.bud-wait{ font-size:var(--fs-1); color:var(--ink-mute); }

  /* ---- read it if you want it ---- */
.score{ width:100%; text-align:left; background:none; border:none; cursor:pointer; }
.score .sc-more{ font-size:var(--fs-1); color:var(--ink-soft); font-weight:var(--fw-hi); margin-top:var(--space-1); display:block; }
.boost{ display:flex; align-items:center; gap:var(--space-2); width:100%; text-align:left;
    padding:var(--space-3) var(--space-4); border-radius:var(--r-control); margin-bottom:var(--space-2);
    background:var(--surface); border:1px solid var(--line); }
.boost .bo-t{ font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink); flex:1; line-height:1.4; }
.boost .bo-go{ color:var(--ink-mute); flex-shrink:0; }
.boost:active{ background:var(--surface-2); }
.detail{ max-width:440px; }
.det-val{ font-family:var(--font-num); font-size:var(--fs-numeric); font-weight:var(--fw-hi); letter-spacing:-.04em; line-height:1;
    color:var(--ink); margin:var(--space-1) 0 var(--space-3); font-variant-numeric:tabular-nums; }
.det-val small{ font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink-mute); letter-spacing:0; }
.det-body{ font-size:var(--fs-3); line-height:1.65; color:var(--ink-soft); margin:0; }

  /* A checkbox with a sentence beside it, used where a choice needs explaining
     rather than just naming. The generic input rule strips the native control,
     so the box has to ask for it back. */
.check{ display:flex; gap:var(--space-3); align-items:flex-start; margin-bottom:var(--space-3);
    font-size:var(--fs-2); line-height:1.55; color:var(--ink-soft); cursor:pointer; }
.check input[type="checkbox"]{
    width:19px; height:19px; min-width:19px; margin:var(--space-1) 0 0; padding:0;
    flex-shrink:0; accent-color:var(--ink-soft);
    -webkit-appearance:checkbox; appearance:checkbox;
    border:none; border-radius:0; background:none; }

  /* ---- first run: one question, then the answer working ---- */
.onboard.intro{ max-width:440px; }
.pain{ display:block; width:100%; text-align:left; padding:var(--space-3) var(--space-4);
    border-radius:var(--r-control); margin-bottom:var(--space-2);
    background:var(--surface); border:1px solid var(--line); }
.pain:active{ background:var(--surface-2); border-color:var(--line); }
.pain-t{ display:block; font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink); }
.pain-s{ display:block; font-size:var(--fs-2); color:var(--ink-mute); line-height:1.5; margin-top:var(--space-1); }

.ba{ margin:var(--space-4) 0 var(--space-5); }
.ba-row{ display:flex; align-items:flex-start; gap:var(--space-3); padding:var(--space-3) var(--space-4);
    border-radius:var(--r-control); }
.ba-row.before{ background:var(--surface); border:1px solid var(--line); }
.ba-row.after{ background:var(--surface-2); border:1px solid var(--line); }
.ba-lbl{ font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.06em; text-transform:uppercase;
    flex-shrink:0; padding-top:var(--space-1); width:74px; }
.ba-row.before .ba-lbl{ color:var(--ink-mute); }
.ba-row.after .ba-lbl{ color:var(--ink-soft); }
.ba-txt{ font-size:var(--fs-3); line-height:1.5; color:var(--ink-soft); }
.ba-row.after .ba-txt{ color:var(--ink); font-weight:var(--fw-med); }
.ba-arrow{ display:flex; justify-content:center; padding:var(--space-2) 0; color:var(--ink-mute); }
.ba-arrow svg{ width:19px; height:19px; }
@media (prefers-reduced-motion:no-preference){
.ba-row.after{ animation:baIn var(--dur-base) var(--ease) both .25s; }
.ba-arrow{ animation:baIn var(--dur-base) var(--ease) both .12s; }
@keyframes baIn{from{ opacity:0; transform:translateY(7px); }to{ opacity:1; transform:none; } }
  }


.sheet.help{ max-width:440px; }
.help-b{ font-size:var(--fs-3); line-height:1.6; color:var(--ink-soft); margin:0 0 var(--space-4); }
.demo{ border-radius:var(--r-control); background:var(--surface);
    border:1px solid var(--line); padding:var(--space-1) var(--space-1); margin-bottom:var(--space-4); }
.demo-l{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2) var(--space-3); font-size:var(--fs-2);
    color:var(--ink-soft); line-height:1.45; }
.demo-l + .demo-l{ border-top:1px solid var(--line); }
.demo-l i{ width:20px; height:20px; border-radius:50%; flex-shrink:0; font-style:normal;
    display:grid; place-items:center; font-size:var(--fs-1); font-weight:var(--fw-hi);
    background:var(--surface-2); color:var(--ink-soft); }
.demo-l:last-child span{ color:var(--ink); font-weight:var(--fw-med); }
@media (prefers-reduced-motion:no-preference){
.demo-l{ animation:demoIn var(--dur-base) var(--ease) both; }
@keyframes demoIn{from{ opacity:0; transform:translateX(-6px); }to{ opacity:1; transform:none; } }
  }

  /* ---- the guide: a line each, the rest behind a tap ---- */
.man-e{ padding:var(--space-4) 0; border-bottom:1px solid var(--line); }
.man-e:last-child{ border-bottom:none; }
.man-q{ font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink); margin-bottom:var(--space-1); }
.man-g{ font-size:var(--fs-2); line-height:1.55; color:var(--ink-soft); }
.man-more{ font-size:var(--fs-2); line-height:1.6; color:var(--ink-soft); margin-top:var(--space-2);
    padding-left:var(--space-3); border-left:2px solid var(--line); }
.man-demo{ margin-top:var(--space-2); }

  /* ---- the walkthrough: every feature, on sample data ---- */
.tdemo{ border-radius:var(--r-control); padding:var(--space-3) var(--space-4); margin:var(--space-4) 0 var(--space-4);
    background:var(--surface); border:1px solid var(--line); text-align:left; }
.td-cat{ font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.06em; text-transform:uppercase;
    color:var(--tide); margin-bottom:var(--space-1); }
.td-task{ font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink); line-height:1.35; }
.td-why{ font-size:var(--fs-2); color:var(--ink-mute); margin-top:var(--space-1); line-height:1.5; }
.td-btn{ margin-top:var(--space-3); padding:var(--space-3); border-radius:var(--r-pill); text-align:center;
    font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--accent-ink);
    background:var(--surface-2); }
.td-note{ font-size:var(--fs-1); color:var(--ink-mute); line-height:1.5; margin-top:var(--space-3); }
.td-fire{ display:flex; gap:var(--space-2); align-items:baseline; margin-top:var(--space-2); }
.td-coin{ font-size:var(--fs-6); font-weight:var(--fw-hi); color:var(--ink-soft); letter-spacing:-.02em; }
.td-xp{ font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink-soft); }
.td-app{ display:flex; justify-content:space-between; align-items:center;
    padding:var(--space-2) 0; font-size:var(--fs-3); color:var(--ink-soft); }
.td-app + .td-app{ border-top:1px solid var(--line); }
.td-shut{ font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.05em; text-transform:uppercase;
    color:var(--danger); }
.td-said{ font-size:var(--fs-3); font-style:italic; color:var(--ink-soft); line-height:1.5; }
.td-arrow{ text-align:center; color:var(--ink-mute); font-size:var(--fs-4); padding:var(--space-1) 0; }
.td-day{ display:flex; gap:var(--space-3); align-items:baseline; padding:var(--space-2) 0; font-size:var(--fs-2);
    color:var(--ink-soft); }
.td-day + .td-day{ border-top:1px solid var(--line); }
.td-day b{ width:38px; flex-shrink:0; color:var(--ink); font-size:var(--fs-2); }
.td-day.gone{ opacity:.45; text-decoration:line-through; }
.td-row{ display:flex; justify-content:space-between; align-items:baseline;
    padding:var(--space-2) 0; font-size:var(--fs-2); color:var(--ink-soft); }
.td-row + .td-row{ border-top:1px solid var(--line); }
.td-row b{ font-size:var(--fs-4); font-weight:var(--fw-hi); color:var(--ink); }
.td-gold{ color:var(--ink-soft) !important; }
.td-score{ display:flex; align-items:center; gap:var(--space-2); padding:var(--space-2) 0; font-size:var(--fs-2);
    color:var(--ink-soft); }
.td-score b{ font-size:var(--fs-5); font-weight:var(--fw-hi); color:var(--ink); width:34px; text-align:right; }
.td-bar{ flex:1; height:6px; border-radius:var(--r-pill); background:var(--line); overflow:hidden; }
.td-bar i{ display:block; height:100%; border-radius:var(--r-pill); background:var(--surface-2); }
.td-hdr{ display:flex; justify-content:space-between; align-items:center;
    font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.06em; text-transform:uppercase;
    color:var(--ink-mute); }
.td-q{ width:24px; height:24px; border-radius:50%; display:grid; place-items:center;
    color:var(--ink-soft); background:var(--surface-2); border:1px solid var(--line); }
.td-q svg{ width:14px; height:14px; }
@media (prefers-reduced-motion:no-preference){
.td-app, .td-day, .td-score{ animation:tdIn var(--dur-base) var(--ease) both; }
.td-fire{ animation:tdPop var(--dur-base) var(--ease) both .15s; }
@keyframes tdIn{from{ opacity:0; transform:translateX(-7px); }to{ opacity:1; transform:none; } }
@keyframes tdPop{from{ opacity:0; transform:scale(.86); }to{ opacity:1; transform:none; } }
  }

  /* ---- planning with someone ---- */
.planrow{ position:relative; padding:var(--space-3) var(--space-4); border-radius:var(--r-control); margin-bottom:var(--space-2);
    background:var(--surface); border:1px solid var(--line); }
.planrow.agreed{ border-color:var(--line); background:var(--surface-2); }
  /* Waiting on you is the only one of these that is a question. It is marked
     by weight and position rather than an accent edge: the accent is reserved
     for the next action, and an accent used as a border is decoration by the
     time you have two of them on a screen. */
.planrow.asking{ border-color:var(--ink-soft); background:var(--surface); }
  /* It did not happen, and it is still here, because a plan someone could not
     make on Tuesday needs a route to Thursday. Quieter than a live one and no
     warning colour: nothing went wrong, a time did not work. */
.planrow.fell{ background:var(--ground); border-style:dashed; }
.planrow.fell .pl-what{ color:var(--ink-soft); }
  /* Notices. The count appears when there is unread mail and goes when there is
     not, so it is a thing that resolves rather than a permanent marker. A badge
     you can clear is information; one that is always there is a tax charged
     every session, which is the version this deliberately is not. */
.mailbtn{ position:relative; width:36px; height:36px; display:grid; place-items:center;
    border-radius:var(--r-control); background:none; color:var(--ink-soft); }
.mailbtn svg{ width:20px; height:20px; }
.mailbtn:active{ background:var(--surface-2); }

/* Chat, D56. A conversation is a run of lines under a rule, like every other
   region: each message is its words on a hairline edge, the person's own on
   the far edge, and nothing sits in a bubble. The cloud is a count, the one
   filled mark, in the same ink the notices count already used. */
.chatrow{ align-items:center; }
.chatcloud{
  margin-left:auto; min-width:22px; height:22px; padding:0 var(--space-2);
  display:grid; place-items:center; border-radius:var(--r-pill);
  background:var(--ink); color:var(--ground);
  font-size:var(--fs-1); font-weight:var(--fw-hi);
}
.chathead{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-mute); margin:var(--space-4) 0 var(--space-2); }
.chattop{ display:flex; align-items:center; gap:var(--space-3); }
.chattop .chathead{ margin:0; color:var(--ink); font-size:var(--fs-3); }
.chatmsgs{
  display:flex; flex-direction:column; gap:var(--space-3);
  max-height:50dvh; overflow-y:auto; overscroll-behavior:contain;
  padding:var(--space-3) 0; border-top:1px solid var(--line);
}
.chatmsg{ max-width:85%; padding:0 var(--space-3); border-left:2px solid var(--line); }
.chatmsg.is-mine{ align-self:flex-end; border-left:none; border-right:2px solid var(--line-strong); text-align:right; }
.chatmsg-who{ display:block; font-size:var(--fs-1); font-weight:var(--fw-hi); color:var(--ink-mute); }
.chatmsg-t{ margin:0; white-space:pre-wrap; overflow-wrap:anywhere; color:var(--ink); }
.chatmsg-at{ font-size:var(--fs-1); color:var(--ink-mute); }
.chatcompose{ margin-top:var(--space-3); }
/* D107: the password box a device without its chat key shows in place of the composer. */
.chatunlock{ margin-top:var(--space-3); }
.chatacts{ display:flex; flex-wrap:wrap; gap:var(--space-2); margin-top:var(--space-2); }
.chatsay{ margin:var(--space-1) 0 0; font-size:var(--fs-2); color:var(--ink-mute); }
.chatreport{ margin-top:var(--space-3); }
.chatreport summary{ font-size:var(--fs-2); color:var(--ink-mute); }
.chatreport .chatacts{ margin-top:var(--space-2); }
  /* Ink, not the accent. "Never an icon, a badge or a tint" is the rule and
     this was a badge; the signal is spent on the next action, and a count of
     unread notices is not that. Ink on the ground is the highest contrast in
     the system and costs no colour. */
.mailnum{ position:absolute; top:2px; right:2px; min-width:16px; height:16px; padding:0 4px;
    border-radius:var(--r-pill); background:var(--ink); color:var(--ground);
    font-size:var(--fs-1); font-weight:var(--fw-hi); line-height:16px; text-align:center;
    font-variant-numeric:tabular-nums; }

.mailrow{ border-bottom:1px solid var(--line); }
.mailrow:last-child{ border-bottom:none; }
.mail-head{ width:100%; text-align:left; padding:var(--space-3) var(--space-1);
    background:none; display:flex; flex-direction:column; gap:2px; }
.mail-t{ font-size:var(--fs-3); font-weight:var(--fw-med); color:var(--ink); }
.mailrow.unread .mail-t{ font-weight:var(--fw-hi); }
.mail-d{ font-size:var(--fs-1); color:var(--ink-mute); letter-spacing:.03em; text-transform:uppercase; }
.mail-body{ padding:0 var(--space-1) var(--space-3); }
.mail-body p{ font-size:var(--fs-3); color:var(--ink-soft); line-height:1.55;
    margin:0 0 var(--space-3); }

  /* The day header expands its day, so it is a button. It was a div, which
     meant no keyboard, no announcement of what it does, and no way to know
     whether the day was already open. */
  /* A row that expands what is under it. The shared shape for full-width
     controls that must not look like buttons: setmenu-row and acct-row are the
     same family. Declared as row-expand so it is reusable and so the primitives
     check can see which family it belongs to. */
.row-expand{ width:100%; text-align:left; background:none; border:0;
    color:inherit; font:inherit; cursor:pointer; }
.row-expand:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

  /* Where a task goes next. Quiet until approached, like the destroy control
     beside it, because parking something should not feel like an event. */
  .row-bucket{
    font-size:var(--fs-1); color:var(--ink-mute); background:none;
    border:1px solid var(--line); border-radius:var(--r-pill);
    padding:2px var(--space-2); cursor:pointer; white-space:nowrap;
    transition:color var(--dur-base) var(--ease), border-color var(--dur-base) var(--ease);
  }
  .row-bucket:hover{ color:var(--tide); border-color:var(--tide); }

  /* A fold, for the tasks that are not the three in front of you. */
  .taskrest{ margin-top:var(--space-3); }
  .taskrest summary{
    cursor:pointer; padding:var(--space-3) 0; font-weight:var(--fw-hi);
    color:var(--ink-soft); list-style:none;
  }
  .taskrest summary::-webkit-details-marker{ display:none; }
  .taskrest summary::before{ content:"\25B8 "; color:var(--ink-mute); }
  .taskrest[open] summary::before{ content:"\25BE "; }
  .taskrest summary em{
    display:block; font-style:normal; font-weight:var(--fw-body);
    font-size:var(--fs-2); color:var(--ink-mute);
  }

  /* The friction tag is a button. It looked like a label and now does
     something, so it has to read as pressable without shouting. */
button.fric-tag{ cursor:pointer; border:1px solid var(--line); background:none;
    color:inherit; font:inherit; padding:0 var(--space-1); border-radius:var(--r-pill); }
button.fric-tag:active{ transform:scale(.96); }

  /* Settings as a menu. Six rows instead of a dozen sections in a column:
     finding something should be a choice, not a scroll. */
.setmenu{ display:flex; flex-direction:column; gap:var(--space-2); margin-top:var(--space-3); }
.setmenu-row{
    display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:var(--space-3);
    width:100%; text-align:left; padding:var(--space-4);
    background:var(--surface); border:1px solid var(--line); border-radius:var(--r-control);
    color:var(--ink); transition:background var(--dur-base) var(--ease), transform var(--dur-base) var(--ease);
  }
.setmenu-row:active{ transform:scale(.985); }
.setmenu-row span{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.setmenu-row b{ font-weight:var(--fw-hi); }
.setmenu-row em{ font-style:normal; font-size:var(--fs-2); color:var(--ink-mute); }
.setmenu-go{ color:var(--ink-mute); flex:0 0 auto; }
  /* Back sits in the same place on every pane, so it is somewhere to reach for
     rather than something to look for. */
.setback{
    display:inline-flex; align-items:center; gap:var(--space-1);
    color:var(--tide); font-size:var(--fs-3); padding:var(--space-2) 0; background:none;
  }
.setpane-h{ font-size:var(--fs-5); font-weight:var(--fw-hi); margin:var(--space-1) 0 var(--space-3); }

  /* A challenge task, wherever it appears. Same row on Now and on the
     Challenges screen, because it is the same thing in both places. */
.ch-tasks{ margin-top:var(--space-3); }
.ch-task{
    display:grid; grid-template-columns:1fr auto; gap:var(--space-1) var(--space-3);
    align-items:center; padding:var(--space-3) 0; border-top:1px solid var(--line);
  }
.ch-task-nm{ font-weight:var(--fw-hi); }
.ch-task-meta{ grid-column:1; font-size:var(--fs-2); color:var(--ink-mute); }
.ch-task button{ grid-row:1 / span 2; }
.ch-task.done .ch-task-nm{ color:var(--ink-mute); text-decoration:line-through; }
  /* Not yet available. Visible, because seeing the whole programme is why
     somebody opened this screen, and dimmed because it is not today's job. */
.ch-task.locked{ opacity:.55; }
.ch-task.locked .ch-task-meta{ font-style:italic; }
/* A window that closed unfinished. Struck through rather than reddened: this
   is a fact about a day that has gone, not a telling off, and the row keeps
   showing what was asked so the record stays honest. */
.ch-task.shut{ opacity:.5; }
.ch-task.shut .ch-task-nm{ text-decoration:line-through; text-decoration-thickness:1px; }
.ch-task.shut .ch-task-meta{ font-style:italic; }

/* Joined late but still in it. Quieter than the amber one, because this is
   context rather than a consequence. */
.ch-note{
  background:var(--tide-wash); border-left:2px solid var(--tide);
  border-radius:var(--r-control); padding:var(--space-3);
  margin-bottom:var(--space-3); font-size:var(--fs-2);
  color:var(--ink-soft); line-height:1.6;
}

/* Said once at the top of the challenge, and it switches nothing off. */
.ch-broken{
  background:var(--gold-wash); border-left:2px solid var(--gold);
  border-radius:var(--r-control); padding:var(--space-3);
  margin-bottom:var(--space-3); font-size:var(--fs-2);
  color:var(--ink-soft); line-height:1.6;
}

  /* Present for a screen reader and a crawler, absent for everybody else.
     The app shell had no h1 at all, which is a real accessibility gap and not
     only an SEO one: it is the landmark that says what this page is. */
.sr-only{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
  }

  /* An announcement. Quiet by design: it is the one thing in Sailyn that arrives
     without the person doing anything, so it says its piece and leaves. */
.ann-kind{ font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.05em;
    text-transform:uppercase; color:var(--ink-soft); margin-bottom:var(--space-2); }
.ann-body{ font-size:var(--fs-3); color:var(--ink-soft); line-height:1.55;
    margin:var(--space-2) 0 var(--space-4); }
.section-h.sub{ font-size:var(--fs-3); color:var(--ink-soft); margin-top:var(--space-4); }
.pl-when{ font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.05em; text-transform:uppercase;
    color:var(--ink-soft); }
.pl-what{ font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink); margin-top:var(--space-1); }
.pl-who{ font-size:var(--fs-2); color:var(--ink-mute); margin-top:var(--space-1); line-height:1.45; }
.field textarea{ width:100%; padding:var(--space-3) var(--space-4); border-radius:var(--r-control);
    background:var(--ground); border:1px solid var(--line); color:var(--ink);
    font-family:inherit; font-size:var(--fs-4); line-height:1.5; resize:vertical; }
.field textarea::placeholder{ color:var(--ink-mute); }
.field textarea:focus-visible{ outline:none; border-color:var(--accent); }

  /* ---- something is waiting on you ---- */
.alerts{ margin-bottom:var(--space-4); }
.alertrow{ display:flex; align-items:center; gap:var(--space-3); width:100%; text-align:left;
    padding:var(--space-3) var(--space-4); border-radius:var(--r-control); margin-bottom:var(--space-2);
    background:var(--surface-2); border:1px solid var(--line); }
.alertrow:active{ background:var(--surface); }
.al-ic{ width:34px; height:34px; border-radius:50%; flex-shrink:0; display:grid;
    place-items:center; color:var(--ink-soft); background:var(--surface); }
.al-ic svg{ width:17px; height:17px; }
.al-b{ display:block; min-width:0; }
.al-t{ display:block; font-size:var(--fs-3); font-weight:var(--fw-hi); color:var(--ink); line-height:1.35; }
.al-s{ display:block; font-size:var(--fs-2); color:var(--ink-mute); margin-top:var(--space-1); }
.foot-legal{ display:block; width:100%; font-size:var(--fs-1); color:var(--ink-mute); margin-top:var(--space-3); }

  /* The browser paints the calendar and clock glyphs itself, in its own
     near-black. On a dark field that is invisible, so it is inverted rather
     than left to disappear. */
  html[data-theme="dark"] .field input::-webkit-calendar-picker-indicator,
html[data-theme="dark"] .rt-time::-webkit-calendar-picker-indicator{
    filter:invert(0.85);
  }



  /* Removes the 300ms double-tap-zoom delay on every control. On a product
     built around one tap to start, a third of a second of nothing is the
     difference between responsive and broken. */
button, .segbtn, a.btn, [role="button"]{ touch-action:manipulation; }

  /* A sheet that scrolls should not drag the page behind it once it hits the
     end, which on iOS looks like the whole app coming loose. */
.scrim, .sheet, .onboard{ overscroll-behavior:contain; }

  /* SVG transforms rotate about the element's own box rather than the
     viewport origin, which is what makes them land where they look like they
     should. */
svg [transform]{ transform-box:fill-box; transform-origin:center; }



  /* The roadmap on the landing page. Six things that do not exist yet, each
     with the honest reason. A product that lists only what it has invites the
     reader to find the gap themselves, and finding it after signing up is
     worse than reading it before. */
  .road{ display:grid; gap:var(--space-3); margin-top:var(--space-5); }
  @media (min-width: 720px){ .road{ grid-template-columns:repeat(2, 1fr); } }
  .road-item{
    position:relative; padding:var(--space-4);
    background:var(--surface); border:1px solid var(--line); border-radius:var(--r-card);
  }
  .road-item h3{ font-size:var(--fs-4); font-weight:var(--fw-hi); margin:0 0 var(--space-2); }
  .road-item p{ margin:0; color:var(--ink-soft); line-height:1.6; font-size:var(--fs-3); }
  .road-when{
    display:inline-block; margin-top:var(--space-3);
    font-size:var(--fs-2); color:var(--ink-mute);
    border:1px solid var(--line); border-radius:var(--r-pill);
    padding:2px var(--space-2);
  }
  .road-not{
    margin-top:var(--space-5); color:var(--ink-mute); font-size:var(--fs-2);
    line-height:1.7; max-width:70ch;
  }


  /* The routines that come with the app. Two up, so the names read as a
     short menu rather than a list to get through. */
  .rt-templates{ display:grid; grid-template-columns:repeat(2,1fr); gap:var(--space-2); }
  .rt-template{
    text-align:left; padding:var(--space-3);
    background:var(--surface); border:1px solid var(--line);
    border-radius:var(--r-control); color:inherit; cursor:pointer;
  }
  .rt-template b{ display:block; font-weight:var(--fw-hi); }
  .rt-template em{ display:block; font-style:normal; font-size:var(--fs-2); color:var(--ink-mute); margin-top:2px; }
  .rt-template:active{ transform:scale(.985); }

  /* One step of a routine. Same shape as a session: one thing on screen, the
     decision already made, and the rest out of sight until it is its turn. */
  .ro-count{ font-size:var(--fs-2); color:var(--ink-mute); margin-bottom:var(--space-2); }
  /* Three lines are kept for every step, so Next and Stop here stay where the
     thumb is: a step one line shorter than the last moved Stop here up under
     it, and the second of two quick taps on Next ended the routine. */
  .ro-step{
    font-size:var(--fs-5); font-weight:var(--fw-hi); letter-spacing:-0.02em;
    text-wrap:balance; margin:0 0 var(--space-4);
    line-height:1.2; min-height:3.6em; display:flex; align-items:flex-end;
  }
  .ro-bar{
    height:4px; border-radius:var(--r-pill); background:var(--line);
    overflow:hidden; margin-bottom:var(--space-4);
  }
  .ro-bar i{ display:block; height:100%; background:var(--tide); transition:width var(--dur-base) var(--ease); }
  /* A routine with steps says so, quietly, so nobody taps expecting a tick. */
  .chip.rt-guided .rt-box::after{ content:"\203A"; color:var(--ink-mute); }


  /* A goal that has drifted. Amber rather than red: this is a decision to make,
     not a failure to report, and the difference has to be visible before the
     words are read. */
  .goal-behind{
    background:var(--gold-wash); border:1px solid var(--gold);
    border-radius:var(--r-card); padding:var(--space-4); margin-bottom:var(--space-3);
  }
  .gb-name{ font-weight:var(--fw-hi); margin-bottom:var(--space-1); }
  .gb-line{ color:var(--ink-soft); font-size:var(--fs-3); margin-bottom:var(--space-3); }
  .gb-acts{ display:flex; gap:var(--space-2); flex-wrap:wrap; }
  .gb-acts .btn{ flex:1; min-width:11ch; }

/* Volume, on the session screen.

   Sits under the now-playing strip so the two read as one control group. The
   buttons are there because a slider is a poor target on a phone held one
   handed, which is how most of these sessions are actually run. */
#app .envvol {
  display: flex; align-items: center; gap: var(--space-2);
  margin: var(--space-2) auto 0; max-width: 260px;
}
#app .env-quiet {
  width: 32px; height: 32px; flex: none;
  display: grid; place-items: center;
  border: 0; background: transparent; color: var(--ink-mute);
  border-radius: var(--r-control); cursor: pointer;
}
#app .env-quiet svg { width: 17px; height: 17px; }
@media (hover: hover) and (pointer: fine) {
  #app .env-quiet:hover { color: var(--ink); background: var(--surface-2); }
}
#app .env-quiet:focus-visible { box-shadow: var(--focus-ring); outline: none; }
#app .envvol input[type="range"] {
  flex: 1; min-width: 0; height: 28px;
  -webkit-appearance: none; appearance: none; background: transparent;
}
#app .envvol input[type="range"]::-webkit-slider-runnable-track {
  height: 4px; border-radius: var(--r-pill); background: var(--line);
}
#app .envvol input[type="range"]::-moz-range-track {
  height: 4px; border-radius: var(--r-pill); background: var(--line);
}
/* 26px, not the 14px a slider usually gets: this is adjusted mid-session by
   somebody who is deliberately not looking at their phone. */
#app .envvol input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 26px; height: 26px; margin-top: -11px;
  border-radius: 50%; background: var(--surface-solid);
  border: 1px solid var(--line); box-shadow: var(--shadow-2); cursor: pointer;
}
#app .envvol input[type="range"]::-moz-range-thumb {
  width: 26px; height: 26px; border: 1px solid var(--line);
  border-radius: 50%; background: var(--surface-solid); box-shadow: var(--shadow-2);
}
#app .envvol input[type="range"]:focus-visible { outline: none; }
#app .envvol input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: var(--focus-ring); }


  /* The options Sailyn did not suggest. Set apart from the ones it did, and
     quieter, so the recommendation still reads as the recommendation while
     everything stays reachable. */
  .setup-more{ margin-top:var(--space-5); padding-top:var(--space-4); border-top:1px solid var(--line-soft); }
  .setup-more .obq-b{ color:var(--ink-mute); }

  /* A permission problem, said where somebody can read it while looking at the
     control they need to click. A toast that vanishes in two seconds is not
     instructions. */
  .place-problem{
    margin-top:var(--space-2); padding:var(--space-3);
    background:var(--gold-wash); border-left:2px solid var(--gold);
    border-radius:var(--r-control); line-height:1.6; color:var(--ink-soft);
  }
  .place-problem[hidden]{ display:none; }


  /* What each door costs, said before somebody walks through it.
     Thirteen questions with no sense of how long is a lot of deciding for
     people who came here because deciding is hard. */
  .setup-door-min{
    display:block; font-style:normal; font-weight:var(--fw-body);
    font-size:var(--fs-2); color:var(--ink-mute); margin-top:2px;
  }
  /* And how much is left, once inside. */
  .obq-left{ display:block; font-size:var(--fs-1); color:var(--ink-mute); font-weight:var(--fw-body); }

  /* The challenge menu. One line each, so being in three does not mean
     scrolling past two to reach the third. */
  .ch-menu{ display:flex; flex-direction:column; gap:var(--space-2); }
  .ch-card{
    display:grid; grid-template-columns:1fr auto; gap:var(--space-1) var(--space-3);
    width:100%; text-align:left; padding:var(--space-4);
    background:var(--surface); border:1px solid var(--line);
    border-radius:var(--r-card); color:inherit; cursor:pointer;
  }
  .ch-card:active{ transform:scale(.99); }
  .ch-card-top{ display:flex; align-items:center; gap:var(--space-2); min-width:0; }
  .ch-card-nm{ font-weight:var(--fw-hi); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .ch-card-in{
    font-size:var(--fs-1); color:var(--ink-mute); border:1px solid var(--line);
    border-radius:var(--r-pill); padding:0 var(--space-2); flex:0 0 auto;
  }
  .ch-card-sub{ grid-column:1; font-size:var(--fs-2); color:var(--ink-mute); }
  .ch-card-go{ grid-row:1 / span 2; align-self:center; font-size:var(--fs-2); color:var(--ink-mute); }


  /* A chosen row, said by the row rather than by a chip on the far right.
   *
   * Selecting a buddy set aria-pressed and added a small "Included" chip, and
   * that was all: the row itself looked exactly like an unselected one. On a
   * list of names the eye reads the rows, not the right margin, so picking
   * three people looked like picking none.
   *
   * .row-title had no rule at all either, so the name was unstyled text.
   */
  .row-title{ font-weight:var(--fw-med); }
  /* A wash alone reads as a hover on a bright screen, and this is a decision
     rather than a hint: three names picked out of six is the whole content of
     the step. So the row gets an outline as well, drawn with a ring rather
     than a border so nothing shifts by a pixel when it is picked. */
  .row[aria-pressed="true"]{
    background:var(--accent-wash);
    border-radius:var(--r-control);
    box-shadow: inset 0 0 0 2px var(--accent);
    padding-left:var(--space-3); padding-right:var(--space-3);
    margin-left:calc(var(--space-3) * -1); margin-right:calc(var(--space-3) * -1);
  }
  .row[aria-pressed="true"] .row-title{ color:var(--accent); font-weight:var(--fw-hi); }
  /* The divider between rows would cut through the highlight. */
  .row[aria-pressed="true"] + .row,
  .row:has(+ .row[aria-pressed="true"]){ border-top-color:transparent; }


  /* Why a plan is sitting in the past. Nobody said no; the hour arrived and
     went, which feels completely different from being declined and is usually
     a plan that was still worth having. */
  .plan-why{ font-size:var(--fs-2); color:var(--ink-mute); margin-top:var(--space-1); }


  /* Starting, offered before the ways of adding to a list. A different kind of
     answer from the two below it, so it sits apart rather than becoming a third
     button in a row of them. */
  .sailnow-wrap{ margin-bottom:var(--space-5); text-align:center; }
  .sailnow-wrap .fnote{ margin-top:var(--space-2); }


  /* Reachable on the day it is needed, invisible on the several hundred days it
     is not. A red button near the timer would be alarming at first and
     wallpaper by the second week. */
  .sos-link{ margin-top:var(--space-6); text-align:center; }
  /* A real .btn, quietened here rather than a bare button of its own. Every
     other button in Sailyn is one, and a hand-rolled one drifts from the rest on
     its own schedule, which on this screen means the day somebody needs it. */
  /* Red, and visible on sight.
     It was a quiet underlined line, on the reasoning that a red button near a
     focus timer becomes wallpaper by the second week. That reasoning is real
     but it loses to the other one: somebody in trouble should not have to hunt,
     and a control they cannot find is a control that does not exist. */
  .sos-btn{
    background:var(--bad-wash); color:var(--bad);
    border:1.5px solid var(--bad); border-radius:var(--r-control);
    padding:var(--space-3) var(--space-5); font-weight:var(--fw-hi);
    min-height:44px;
  }
  .sos-btn:active{ background:var(--bad); color:var(--surface); }

  /* The sheet. Slightly wider than an onboarding card, because three phone
     numbers at a readable size need the room and nothing here should wrap. */
  .onboard.sos{ max-width: 34rem; }

  /* The screen itself. Written for somebody who is not reading carefully: the
     numbers are the biggest thing on it and each is one tap. */
  .sos-call{
    display:block; width:100%; text-align:left; text-decoration:none;
    padding:var(--space-4); margin-top:var(--space-2);
    background:var(--surface-2); border:1px solid var(--line);
    border-radius:var(--r-card); color:inherit; min-height:44px;
  }
  .sos-call-n{ display:block; font-size:var(--fs-6); font-weight:var(--fw-hi); letter-spacing:0.01em; overflow-wrap:anywhere; }
  /* An address rather than a number: a size down, so findahelpline.com fits a
     phone on one line instead of running out of the sheet. */
  .sos-call-url{ font-size:var(--fs-4); overflow-wrap:normal; }
  .sos-call-l{ display:block; font-size:var(--fs-2); color:var(--ink-mute); margin-top:2px; line-height:1.5; }
  /* Emergency reads differently from the crisis line, because they are
     different situations and nobody should have to work out which is theirs. */
  .sos-call--urgent{ background:var(--bad-wash); border-color:var(--bad); }
  .sos-call--urgent .sos-call-n{ color:var(--bad); }


  /* Said where somebody is deciding what to type, not only in the policy. A
     person about to write something personal into a box is not reading the
     privacy page first. */
  .ai-note{ margin-top:var(--space-2); }

/* Sailing with somebody, above the card rather than under it.
   Quieter than Start, which is still the thing to press, and separated so it
   does not read as another option belonging to the task. */
.sailwith{ margin-bottom: var(--space-3); }

/* A plan somebody called off.
   Struck through so it reads as ended at a glance rather than as one more
   thing to deal with, and dimmed, because it is no longer work. The cross is
   the only control: suggesting a new time here would be offering to reopen a
   decision that was already taken. */
.planrow.off{ position:relative; opacity:0.72; }
.planrow.off .plan-title{ text-decoration:line-through; text-decoration-thickness:1.5px; }
.planrow.off .plan-x{
  position:absolute; top:var(--space-3); right:var(--space-3);
  width:30px; height:30px; display:grid; place-items:center;
  background:transparent; border:0; border-radius:50%;
  color:var(--ink-mute); cursor:pointer;
}
.planrow.off .plan-x svg{ width:16px; height:16px; }
.planrow.off .plan-x:active{ background:var(--surface); color:var(--ink); }
@media (hover:hover) and (pointer:fine){
  .planrow.off .plan-x:hover{ background:var(--surface); color:var(--ink); }
}
.planrow.off .plan-x:focus-visible{ box-shadow:var(--focus-ring); outline:none; }

/* Device setup, on the front page of Settings.
   Warm when something needs doing and quiet when it does not, because a card
   that always looks urgent is a card people stop reading. */
.devsetup{ border:1px solid var(--line); border-radius:var(--r-card);
  padding:var(--space-4); margin:0 0 var(--space-4); background:var(--surface); }
/* The wash marks it, not a border. The accent is the next action, and a
   coloured outline round a container is exactly the decoration themecheck
   forbids: the button inside is already the accent, and two accents on one
   card means neither is the thing to press. */
.devsetup.warn{ background:var(--accent-wash); }
.devsetup-h{ display:flex; align-items:baseline; justify-content:space-between;
  gap:var(--space-3); margin-bottom:var(--space-1); }
.devsetup-k{ font-weight:var(--fw-hi); font-size:var(--fs-2); }
.devsetup-dev{ font-size:var(--fs-1); color:var(--ink-mute); }
.devsetup-b{ font-size:var(--fs-1); color:var(--ink-soft);
  line-height:1.55; margin-bottom:var(--space-3); }
.setup-again{ text-align:center; margin:var(--space-2) 0 var(--space-1); }

/* Who you are sitting with, during a session.
   Quiet, above the name, because the point of the screen is still the clock. */
.sess-with{ text-align:center; font-size:var(--fs-1); color:var(--ink-mute);
  margin-bottom:var(--space-2); }

/* Done, from the plan.
   Beside the cross and quieter than it, because removing a task and finishing
   one are opposite acts and the destructive one should not be the calm one. */
.row-done,
.pd-done{
  flex:0 0 auto; width:28px; height:28px; display:grid; place-items:center;
  background:transparent; border:0; border-radius:50%;
  color:var(--ink-mute); cursor:pointer;
}
.pd-done svg{ width:15px; height:15px; }
.pd-done:active{ background:var(--line-soft); color:var(--good); }
@media (hover:hover) and (pointer:fine){
  .pd-done:hover{ background:var(--line-soft); color:var(--good); }
}
.pd-done:focus-visible{ box-shadow:var(--focus-ring); outline:none; }

/* Confirm your address.
   Washed rather than outlined, like the device setup card: the accent belongs
   to the buttons inside it, and two accents on one panel means neither is the
   thing to press. */
.emailnag{
  position:relative; background:var(--accent-wash);
  border-radius:var(--r-card); padding:var(--space-4);
  margin:0 0 var(--space-4);
}
.emailnag-b b{ display:block; font-size:var(--fs-2); margin-bottom:var(--space-1); }
.emailnag-b span{ display:block; font-size:var(--fs-1); color:var(--ink-soft); line-height:1.55; }
.emailnag-x{
  position:absolute; top:var(--space-2); right:var(--space-2);
  width:28px; height:28px; display:grid; place-items:center;
  background:transparent; border:0; border-radius:50%; color:var(--ink-mute); cursor:pointer;
}
.emailnag-x svg{ width:14px; height:14px; }
.emailnag-x:active{ background:var(--surface); color:var(--ink); }
@media (hover:hover) and (pointer:fine){ .emailnag-x:hover{ background:var(--surface); color:var(--ink); } }
.emailnag-x:focus-visible{ box-shadow:var(--focus-ring); outline:none; }

/* A picked chip, unmistakably picked.
   --accent-wash is nine per cent opacity, which is a hint rather than an
   answer: on a bright screen a picked category looked the same as the six
   beside it. The ring is what makes it readable, drawn inset so nothing
   shifts by a pixel when it is tapped. */
.chip--on{
  box-shadow: inset 0 0 0 2px var(--accent);
  font-weight: var(--fw-hi);
}


/* =========================================================================
   THE WORLD LAYER.

   Everything above builds components. This block enforces the three rules the
   world is made of, and it is last on purpose: it overrides component-local
   decisions that predate the system rather than requiring 71 call sites to be
   found and edited by hand.

   1. Rules, not boxes.
   2. One band.
   3. Numbers are the voice.
   ========================================================================= */

/* ---- 3. Numbers are the voice -------------------------------------------
   Every duration, clock time, count and balance in one tabular face. The old
   rule set tabular figures but left the family as the UI face, so a counting
   timer still shifted width as digits changed shape. */
.num, time, [data-num],
.sess-time, .rw-amt, .stat-n, .bignum,
.load-val, .skill-lv, .pd-load, .stat-v, .rank-h{
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: -0.01em;
}

/* ---- Screen title -------------------------------------------------------
   Moved off the display serif. A serif heading sitting above an instrument
   field was two worlds on one screen; the grotesk at 40px with real negative
   tracking does the same job and belongs to the same object as the numbers
   underneath it. The serif keeps the wordmark, which is the brand, and
   nothing else. */
.screen-title{
  font-family: var(--font-ui);
  font-size: var(--fs-6);
  font-weight: var(--fw-hi);
  letter-spacing: var(--track-title);
  line-height: 1.05;
  color: var(--ink);
  margin: 0;
}

/* Small structural labels read as instrument legends rather than as prose:
   upper case, tracked out, quiet. */
.section-h, .screen-sub .lbl, .fieldlabel{
  letter-spacing: var(--track-label);
}

/* ---- 1. Rules, not boxes ------------------------------------------------
   Content regions lose their outline and their fill and keep a single hairline
   above them. Anything that is genuinely a control keeps its box, because an
   input without an edge is not obviously a place you can type, and that is an
   affordance question rather than a decoration one.

   Listed by class rather than applied globally: a blanket rule would strip the
   inputs and the sheets too. */
#app main .cls,
#app main .forecast,
#app main .pday,
#app main .boost,
#app main .outlook,
#app main .load,
#app main .insight,
#app main .statbox,
#app main .revcard,
.cls, .forecast, .pday, .boost, .outlook, .load, .insight, .statbox, .revcard{
  background: none;
  border: none;
  border-top: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  padding-left: 0;
  padding-right: 0;
}

.load-val{ font-size: var(--fs-5); font-weight: var(--fw-hi); color: var(--ink); }
.load-lbl{ letter-spacing: var(--track-label); text-transform: uppercase; font-size: var(--fs-1); color: var(--ink-mute); }

/* A region directly following another does not need two rules between them. */
.pday + .pday{ border-top-color: var(--line-soft); }

/* ---- 2. One band --------------------------------------------------------
   The accent is a field, and at most one region per screen carries it. Where
   the old system tinted a strip to say "informational", the strip now carries
   a single leading rule and no fill, so the only saturated area left on a
   screen is the live band or the next action. */
#app main .mode-strip,
.mode-strip{
  background: none;
  border: none;
  border-left: var(--band-w) solid var(--tide);
  border-radius: 0;
  padding: var(--space-3) var(--space-4);
  color: var(--ink-soft);
}

/* ---- Affordance ---------------------------------------------------------
   The tertiary button was background:none with no edge, so on the Now screen
   three of the five actions rendered as centred paragraphs and only Start
   looked pressable. A tertiary action is still quiet, but it has to be
   visibly a control: a full-width row with a hairline, its label in the
   reading weight, and a real pressed state. */
.btn--tertiary{
  background: none;
  color: var(--ink-soft);
  font-weight: var(--fw-med);
  border: 1px solid var(--line);
  border-radius: var(--r-control);
  padding-left: var(--space-4);
  padding-right: var(--space-4);
  transition: border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
@media (hover: hover) and (pointer: fine){
  .btn--tertiary:not(:disabled):hover{
    border-color: var(--line-strong);
    color: var(--ink);
  }
}
.btn--tertiary:not(:disabled):active{
  color: var(--ink);
  border-color: var(--line-strong);
}

/* The segmented length picker read as three text inputs because each option
   carried an input-shaped box. One box around the group, hairlines between
   the options, and the chosen one inverts. That is a control, and it is also
   rule 1 applied to a control rather than to a region. */
.segrow{
  border: 1px solid var(--line);
  border-radius: var(--r-control);
  overflow: hidden;
  gap: 0;
}
.segrow > button{
  border: none;
  border-radius: 0;
  background: none;
  flex: 1;
}
.segrow > button + button{ border-left: 1px solid var(--line); }
.segrow > button.on,
.segrow > button[aria-pressed="true"]{
  background: var(--ink);
  color: var(--ground);
  font-weight: var(--fw-hi);
}

/* ---- The time field -----------------------------------------------------
   Duration is length. A block of work is drawn at its real height, so an hour
   is visibly four times a quarter hour and a day that is genuinely full looks
   full before a number says so. Components opt in by setting --mins.
   ------------------------------------------------------------------------- */
.timefield{
  position: relative;
  padding-left: var(--field-gutter);
  border-top: 1px solid var(--line);
}
.timefield .hour{
  position: relative;
  height: var(--hour-h);
  border-bottom: 1px solid var(--line-soft);
}
.timefield .hour::before{
  content: attr(data-hour);
  position: absolute;
  left: calc(var(--field-gutter) * -1);
  top: -0.6em;
  width: calc(var(--field-gutter) - var(--space-3));
  text-align: right;
  font-family: var(--font-num);
  font-size: var(--fs-1);
  color: var(--ink-mute);
}
.timefield .block{
  position: absolute;
  left: 0;
  right: 0;
  /* No min-height. It was 22px, which at --hour-h 64 meant a quarter hour and
     an hour rendered 2.9x apart instead of 4x, and everything under about 21
     minutes came out the same rectangle: exactly the category default the
     whole thesis exists to refuse. A short block is allowed to be short. */
  min-height: 0;
  height: calc(var(--mins, 30) * var(--minute-h));
  border-left: var(--band-w) solid var(--ink);
  background: var(--ground-deep);
  padding: 0 var(--space-3);
  overflow: hidden;
  line-height: 1.5;
}
/* Already finished. Still drawn, in its real place, because the shape of the
   day includes the part that has gone; dimmed, because it is not a decision
   any more. */
.timefield .block.is-past{ opacity: 0.42; }
.timefield .block.is-past .block-n{ font-weight: var(--fw-body); }

/* The one band. Now, and nothing else on the screen. */
.timefield .block.is-now{
  border-left-color: var(--accent);
}
.timefield .nowline{
  position: absolute;
  left: calc(var(--field-gutter) * -1);
  right: 0;
  height: 0;
  border-top: 2px solid var(--accent);
  pointer-events: none;
}

/* ---- Empty states -------------------------------------------------------
   The primitive says one line and one action. Several shipped as two
   paragraphs of centred prose with nothing to press, which is the least
   readable configuration available and leaves the person with no next step.
   Constrain the measure and stop the centring at the second line.

   Task 3.5 stops it at the first line as well. Centred was the one place in
   the product where text did not begin on the gutter every other screen
   begins on, so an empty Plan or Tasks read as a different screen from the
   same screen with one row in it. The horizontal padding goes with it: .prow
   carries none, so 16px here put the sentence a step to the right of the rows
   it replaces, which is the misalignment centring was hiding.

   .empty p in views.css is (0,1,1) and outranks .empty-line at (0,1,0), so
   the measure and the margin that render are that rule's, not this one's.
   Left here so the primitive states its intent, and both agree. */
.empty{ text-align: left; padding: var(--space-6) 0; }
.empty-h{
  font-family: var(--font-ui);
  font-size: var(--fs-5);
  font-weight: var(--fw-hi);
  letter-spacing: var(--track-title);
  margin: 0 0 var(--space-2);
}
.empty-line{
  max-width: 46ch;
  margin: 0;
  color: var(--ink-mute);
  font-size: var(--fs-3);
}
.empty p + p{ margin-top: var(--space-3); }


/* -------------------------------------------------------------------------
   The day field's furniture.

   The head is an instrument legend and one number: how much of the span is
   not already spoken for. That number is the reason the picture is worth
   reading, so it gets the numeric voice and the label does not.
   ------------------------------------------------------------------------- */
.dayfield-head{
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-5);
  padding-bottom: var(--space-2);
}
.dayfield-lbl{
  font-size: var(--fs-1);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
}
.dayfield-clear{
  font-size: var(--fs-2);
  color: var(--ink-soft);
}

/* A block carries its start time and its name on one line. The time is set in
   the numeric face so a column of them aligns on the digit; the name takes
   whatever is left and truncates rather than wrapping, because a block is
   drawn at the height of its minutes and cannot grow to fit its label. */
.timefield .block{
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}
.timefield .block-t{
  font-family: var(--font-num);
  font-size: var(--fs-1);
  color: var(--ink-mute);
  flex: 0 0 auto;
}
.timefield .block-n{
  font-size: var(--fs-2);
  font-weight: var(--fw-med);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.timefield .block.is-now .block-t,
.timefield .block.is-now .block-n{ color: var(--ink); }

/* The last hour row owns the bottom edge of the field. */
.timefield .hour:last-child{ border-bottom-color: var(--line); }

/* -------------------------------------------------------------------------
   The rail, as rules rather than boxes.

   Six outlined rectangles stacked vertically is the one place the old card
   language survived the token swap, and it is the first thing visible on
   desktop. Rule 1 applies to navigation too: a hairline between items, the
   current one marked by a band on its leading edge and by weight.
   ------------------------------------------------------------------------- */
@media (min-width: 880px){
  #app nav button{
    border: none;
    border-radius: 0;
    border-top: 1px solid var(--line-soft);
    background: none;
    padding-left: var(--space-3);
  }
  #app nav button:first-of-type{ border-top: none; }
  #app nav button.on{
    background: none;
    box-shadow: inset var(--band-w) 0 0 var(--accent);
    color: var(--ink);
  }
  #app nav button.on span{ font-weight: var(--fw-hi); }
}


/* -------------------------------------------------------------------------
   Touch targets.

   Measured in a real 390px frame: the header controls came out at 32 to 38px
   and the session-length chips at 32px. The chips are the control people reach
   for most often before starting, and a 32px target on a phone held one-handed
   while walking is the case this product is explicitly built for.

   Scoped to coarse pointers so a mouse-driven desktop keeps the tighter
   header it was designed with. Width as well as height: a 70px-wide pill that
   is 32px tall fails in one axis only, and that is still a miss.
   ------------------------------------------------------------------------- */
@media (pointer: coarse){
  #app header .mailbtn,
  #app header .themebtn,
  #app header .gear,
  #app header .langbtn,
  #app header .hdrcoins,
  #app .chip,
  #app .segrow > button{
    min-height: 44px;
  }
  /* A chip row that grows to 44 needs its own breathing room back, or the
     controls above and below it end up touching. */
  #app .chiprow{ gap: var(--space-2); }
}


/* =========================================================================
   THE NOW SCREEN, COMPOSED.

   What it was: fourteen strips stacked in source order at one weight. An
   extension advert, the greeting at 40px, the date, a mode hint, a load bar, a
   day summary, an hour grid, a full-width "sail with someone", a label reading
   "Next up", and only then the single thing the screen exists to answer. The
   day grid alone was 379px of the first screenful, and the work was below the
   fold on a phone.

   What it is: a head, the work, what follows it, and the shape of the day
   beside them. The work is the only thing at the top of the type scale.
   ========================================================================= */

.nowhead{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--space-4); margin-bottom:var(--space-5);
}
.nowhead-who{ min-width:0; }
.nowhead-acts{ display:flex; align-items:center; gap:var(--space-3); flex-shrink:0; flex-wrap:wrap; justify-content:flex-end; }

/* The greeting steps down, on this screen only.
   .screen-title is --fs-6 everywhere and stays that way: on Tasks, Plan,
   Story and Insights the title is the subject of the screen and should be the
   largest thing on it. On Now the subject is the work, and a person's own name
   set larger than what they are about to do is the hierarchy backwards. */
#main[data-screen="now"] .screen-title{
  font-size:var(--fs-4); font-weight:var(--fw-med); letter-spacing:-0.01em;
}
#main[data-screen="now"] .screen-sub{ margin-bottom:0; }

/* Sailing with someone is an alternative to the work, so it stays above the
   card, as it was. It is no longer a full-width block: at that width, directly
   over the one thing to press, it read as the first of two primary actions. */
.sailwith{ margin-bottom:0; width:auto; min-height:36px; padding-left:0; padding-right:0; }

/* ---- the lead ---- */

/* .nowcard keeps its name and loses its box. It was a bordered, padded panel
   sitting on a page whose whole design rule is that content does not sit on
   panels. The lead does not need separating from anything: it is first. */
.nowcard{
  background:none; border:none; border-radius:0; padding:0;
  position:relative; overflow:visible;
  animation:rise var(--dur-base) var(--ease) both;
}
.lead-cat{ font-weight:var(--fw-hi); }
.lead .blockbar, .lead .forward{ margin-bottom:var(--space-5); }
.lead .mults{ margin-top:var(--space-3); }
.lead .lenpick{ margin-top:var(--space-3); }

/* ---- what follows ---- */

.upnext{ display:flex; flex-direction:column; }
.upnext-row{ gap:var(--space-3); align-items:flex-start; padding:var(--space-3) 0; }
.upnext-row .pdot{ margin-top:7px; }

/* ---- the day, as a region ---- */

.dayregion .today-strip{ margin-bottom:var(--space-4); }
.dayregion .timefield{ margin-bottom:0; }

/* Open time is drawn, not left blank.
   A blank stretch reads as an empty calendar, which is the one thing this
   screen must never look like. The largest open run ahead is a block like any
   other, marked by a dashed leading edge so it reads as space held rather than
   as something scheduled. */
.timefield .block.is-open{
  background:none;
  border-left:var(--band-w) dashed var(--line-strong);
}
.timefield .block.is-open .block-t,
.timefield .block.is-open .block-n{ color:var(--ink-mute); }
.timefield .block.is-open .block-n{ font-weight:var(--fw-med); }

/* Planned work, D60. Placed by Sailyn rather than fixed by somebody else, so it
   keeps the block's shape and loses its weight: no fill, a muted solid edge,
   the name at body weight. A commitment stays the one with the ink edge. */
.timefield .block.is-planned{
  background:none;
  border-left-color:var(--line-strong);
}
.timefield .block.is-planned .block-n{ font-weight:var(--fw-body); }
/* The trip to and from an errand (D131): the same quiet block, dashed, so it
   reads as getting there rather than as work. */
.timefield .block.is-trip{ border-left-style:dashed; }
.timefield .block.is-trip .block-n{ color:var(--ink-mute); }

/* Time kept free, C3. Fixed like a commitment, so nothing is placed over it,
   and empty of work on purpose, so it is drawn the way open time is: a dashed
   edge and no fill, with its name, in the muted ink. */
.timefield .block.is-kept{
  background:none;
  border-left:var(--band-w) dashed var(--line-strong);
}
.timefield .block.is-kept .block-n{ color:var(--ink-mute); font-weight:var(--fw-body); }

/* ---- the advert, demoted ---- */

/* Painted at the end of every screen by the frame now. It is optional, it is
   the same message every time, and it is never the reason anybody opened the
   app, so it reads as a footnote rather than as a banner. */
#app main > .mode-strip:not(.locked){
  margin-top:var(--space-6);
  border:none; border-top:1px solid var(--line-soft);
  background:none; color:var(--ink-mute);
  font-size:var(--fs-2); padding:var(--space-3) 0 0;
}

/* ---- the region rhythm on a phone ---- */

@media (max-width: 879px){
  /* The context column follows the work rather than sitting beside it, so it
     needs a rule above it to say a new subject has started. */
.page--split > .page-side{ border-top:1px solid var(--line); padding-top:var(--space-5); }
}


/* A tertiary action inside a composed group is an underline, not a box.
   The full-width bordered treatment is right for a standalone tertiary button,
   which is what it was written for: three of five actions on the old Now
   screen rendered as centred paragraphs and only Start looked pressable. In a
   row beneath a primary, five boxes stacked in a column is the same problem
   the other way up. The affordance stays, as an edge under the label. */
.lead-outs .btn--tertiary,
.sailwith.btn--tertiary,
.region-head .btn--tertiary{
  border-color:transparent;
  border-bottom-color:var(--line);
  border-radius:0;
  padding-left:0; padding-right:0;
  min-height:32px;
}
@media (hover:hover) and (pointer:fine){
  .lead-outs .btn--tertiary:hover,
  .sailwith.btn--tertiary:hover,
  .region-head .btn--tertiary:hover{ color:var(--ink); border-bottom-color:var(--line-strong); }
}

/* Sailing with somebody reads as the same kind of act as sailing alone, so it
   is said in the accent rather than in the quiet ink every other secondary
   action uses. It is the one control on this screen that offers the same thing
   the primary offers, with another person in it.

   Colour only. The accent stays a field on exactly one element per screen and
   that element is still "Sail now": this is a label and a hairline, which is
   what rule 2 permits and what a badge or a tint would not be. */
.sailwith{ color:var(--accent); }
.sailwith.btn--tertiary:not(:disabled):active{ color:var(--accent-deep); }
@media (hover:hover) and (pointer:fine){
  .sailwith.btn--tertiary:hover{ color:var(--accent-deep); border-bottom-color:var(--line-strong); }
}

/* On the empty Now the two sit together, so they are one size.
   "Sail now with someone" was width:auto, 32px tall and unpadded while "Sail
   now" was a full-width 44px block directly above it: two controls offering
   the same act, drawn as if one of them were an afterthought. Matched here on
   width, height, padding and radius so the pair reads as a pair.

   Scoped to .sailnow-wrap on purpose. The other call site puts this button in
   the screen head beside the day control, where it is a compact action and a
   full-width block would take the header apart. */
/* The label takes the accent and the box does not.
   Rule 2 permits the accent as a border only on the live band and on focus, so
   the edge here is the ordinary hairline every tertiary control carries. The
   colour is in the word, which is what was asked for and what a badge or a
   tinted box would not be. */
.sailnow-wrap .sailwith.btn--tertiary{
  width:100%;
  min-height:44px;
  padding:var(--space-3) var(--space-4);
  margin-top:var(--space-3);
  margin-bottom:0;
  border:1px solid var(--line);
  border-radius:var(--r-control);
}
@media (hover:hover) and (pointer:fine){
  .sailnow-wrap .sailwith.btn--tertiary:hover{ border-color:var(--line-strong); }
}

/* =========================================================================
   THE HARBOUR NOW. S5, Task 3.1, behind flags.harbour.

   docs/UI-SPEC.md section 5, top to bottom: the painting across the top 45%
   with the header on it in white, one sentence, one filled button and one
   outlined one, the queue, and then the shape of the day that was always
   here. Nothing below applies until renderHarbourNow() draws, because every
   selector names something only it emits, and the header's data-surface is
   set by the frame only while the hero is on screen.
   ========================================================================= */

/* The header, on the painting. Phone only: from 880 up the header is the
   head of the rail and there is nothing under it but the rail.

   Out of the flow rather than a navy bar above the picture. UI-SPEC puts the
   header on the painting, and the painting is what makes it legible: the top
   of every sky state is --depth. Once the painting has scrolled out from
   under it the frame sets data-past and the instrument ground comes back,
   because white type over a list is not a header, it is a hole in one. */
@media (max-width: 879px){
  #app header[data-surface="harbour"]{
    position:absolute; top:0; left:0; right:0; z-index:var(--z-nav);
    background:none;
    transition:background-color var(--dur-fast) var(--ease);
  }
  #app header[data-surface="harbour"][data-past]{ background:var(--ground); }
  #app header[data-surface="harbour"]:not([data-past]) .brand span,
  #app header[data-surface="harbour"]:not([data-past]) .langbtn,
  #app header[data-surface="harbour"]:not([data-past]) .themebtn,
  #app header[data-surface="harbour"]:not([data-past]) .gear,
  #app header[data-surface="harbour"]:not([data-past]) .mailbtn{
    color:var(--signal); background:none;
  }

  /* Full bleed: out through main's own padding, to both edges and the top.
     The floor is for a phone held sideways, where 45% of the height is a
     strip too thin to be a picture of anything. */
  #main[data-screen="now"] > .harbour--hero{
    margin:calc(-1 * var(--space-1)) calc(-1 * var(--space-5)) 0;
    height:max(12rem, 45svh);
    flex:0 0 auto;
  }
}
/* D48: a narrow screen that is also short, a flip phone's outside screen,
   gives the painting less of its height, so the day's line and the one to do
   now are still on the first screenful. */
@media (max-width: 420px) and (max-height: 560px){
  #main[data-screen="now"] > .harbour--hero{ height:max(9rem, 36svh); }
}
/* UI-SPEC draws no desktop. At rail width the hero is a wide picture at the
   head of the content column.

   It used to sit inside the 760px measure every direct child of main keeps,
   while the two columns under it are exempt and run to the full width from
   1160 up, so the painting, the sentence and the buttons stopped short of the
   day column beside the queue and left a strip of nothing down the right. The
   top of Now spans the same width as the columns under it now, and the two
   Sail buttons share a row, two to one, rather than stretching into bars. */
@media (min-width: 880px){
  #main[data-screen="now"] > *{ max-width:none; }
  /* Decision D38: the painting keeps its own shape. Across the full width it
     was a band nearly three times wider than tall, and a crop that fills its
     box zoomed the 393 by 384 scene to more than twice its size and showed a
     strip through the middle: no water, no hull, no bank, and the requirement
     on the next plot in letters an inch high. So the top of Now is two
     columns, the painting at its own shape on the left and the sentence and
     both Sail buttons beside it, and everything else runs the full width
     under them. The buttons stack, as they do on a phone, because the column
     beside the painting is too narrow for two to one. */
  #main[data-screen="now"]{
    display:grid; grid-template-columns:minmax(0, 1fr) minmax(0, 1fr);
    column-gap:var(--space-7); align-content:start;
  }
  #main[data-screen="now"] > *{ grid-column:1 / -1; }
  #main[data-screen="now"] > .harbour--hero{
    grid-column:1; grid-row:1 / span 4; align-self:start;
    width:100%; max-width:30rem; height:auto; aspect-ratio:393 / 384;
    margin-bottom:var(--space-6);
  }
  #main[data-screen="now"] > .nowline{ grid-column:2; grid-row:1; align-self:end; margin-top:0; }
  /* D41: the one to do now between the day's line and the two Sail buttons. */
  #main[data-screen="now"] > .nowweek{ grid-column:2; grid-row:2; }
  #main[data-screen="now"] > .nowsug{ grid-column:2; grid-row:3; }
  #main[data-screen="now"] > .nowacts{ grid-column:2; grid-row:4; align-self:start; }
}

/* One sentence. Not a title and not a greeting, so it is the UI face at the
   section-heading size and regular weight, never the display serif. */
.nowline{
  font-family:var(--font-ui); font-size:var(--fs-5); font-weight:var(--fw-body);
  color:var(--ink); letter-spacing:-0.01em; line-height:1.25;
  margin:var(--space-6) 0 var(--space-5);
}

.nowacts{ display:flex; flex-direction:column; gap:var(--space-3); margin-bottom:var(--space-6); }

/* The week, under the day's line. Seven days as marks, lit in --gold because a
   day with a sitting in it is something earned, and the count set in the
   numeric face like every other count. An unlit day keeps its box so the row
   reads as a week whether none is lit or all are. */
.nowweek{
  display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2) var(--space-4);
  margin:calc(-1 * var(--space-3)) 0 var(--space-5);
}
.nowweek-l{ font-size:var(--fs-2); font-weight:var(--fw-hi); color:var(--ink-mute); }
.nowweek-n{ font-size:var(--fs-3); color:var(--ink); }
.nowweek-n .num{ font-family:var(--font-num); font-variant-numeric:tabular-nums; font-weight:var(--fw-hi); }
.nowweek-met{ color:var(--gold); font-weight:var(--fw-med); }
.nowweek-days{ display:flex; gap:var(--space-1); }
/* D130, from the Daybreak brief: each day a round mark, a sun, lit when a
   sitting landed on it; today, still open, is a dashed ring. */
.nowweek-d{
  min-width:24px; height:24px; display:grid; place-items:center;
  font-size:var(--fs-1); color:var(--ink-mute);
  border:1px solid var(--line); border-radius:var(--r-pill);
}
.nowweek-d.is-lit{ background:var(--gold-wash); border-color:var(--gold); color:var(--ink); }
.nowweek-d.is-today{ font-weight:var(--fw-hi); color:var(--ink); }
.nowweek-d.is-today:not(.is-lit){ border-style:dashed; border-color:var(--ink-mute); }
.nowweek-today{ flex-basis:100%; font-size:var(--fs-2); color:var(--ink-soft); }
.nowweek-today .num{ font-family:var(--font-num); font-variant-numeric:tabular-nums; color:var(--ink); }

/* Sail now, filled --hull so it reads as a piece of the painting rather than
   as a control in the accent. The only filled thing on the screen.
   --r-control, not the mockup's 8px: UI-SPEC section 12 conflict 1 is a
   founder call and is not settled inside a screen task. */
[data-surface="harbour"].nowsail{
  min-height:52px; gap:var(--space-3);
  background:var(--hull); color:var(--signal);
  font-size:var(--fs-3); font-weight:var(--fw-med);
  border-radius:var(--r-control);
}
.nowacts .nowsail svg{ width:11px; height:14px; }
@media (hover:hover) and (pointer:fine){
  [data-surface="harbour"].nowsail:hover:not(:disabled){ background:var(--depth); }
}
/* The harbour is the same painting in both themes, so --hull does not move,
   and on the dark ground it is within a step of the page: the fill stops
   reading as a fill and the button as a button. A control keeps its box, so
   in the dark theme the edge is drawn, in the same hairline the button under
   it already carries. Not a dark block for the harbour scope: no token is
   restated, and this reads an instrument token. */
html[data-theme="dark"] [data-surface="harbour"].nowsail{ border:1px solid var(--line-strong); }

/* Sail now with someone: same width, no fill, a hairline, near-black. The
   accent label the older Now gave it is the one thing UI-SPEC changes here.

   Selected on .btn--tertiary rather than on .sailwith since Task 3.7, which
   is a widening to what the rule already described: the second action under
   .nowacts is a full-width tertiary, and .sailwith was simply the only one
   there was until the comeback screen drew a second. .sailnow-wrap's own
   .sailwith rules, which take the accent, are a different block and are
   untouched. */
.nowacts .btn--tertiary{
  width:100%; min-height:52px; margin:0;
  padding:var(--space-3) var(--space-4);
  border:1px solid var(--line); border-radius:var(--r-control);
  color:var(--ink);
}
.nowacts .btn--tertiary:not(:disabled):active{ color:var(--ink); }
@media (hover:hover) and (pointer:fine){
  .nowacts .btn--tertiary:hover{ color:var(--ink); background:none; border-color:var(--line-strong); }
}

/* UP NEXT is a section label, not a region title: it names what follows. */
.nowq .region-head{ margin-bottom:0; }
.nowq .region-title{
  font-size:var(--fs-1); font-weight:var(--fw-med); color:var(--ink-mute);
  text-transform:uppercase; letter-spacing:0.08em;
}

/* The two-line row, UI-SPEC section 8. Now is the only screen that uses it.
   The circle is 21px and the control under it is 44, so the ring sits on the
   gutter and the target is still one a thumb can hit. */
.nowrows{ display:flex; flex-direction:column; }
.nowrow{ display:flex; align-items:center; gap:var(--space-1); border-bottom:1px solid var(--line); }
.nowrow-check{
  flex:0 0 auto; width:44px; height:44px; margin-left:-11px;
  display:grid; place-items:center;
  background:none; border:0; padding:0; border-radius:50%; cursor:pointer;
}
.nowrow-check::before{
  content:""; width:21px; height:21px; border-radius:50%;
  border:1px solid var(--ink-mute);
  transition:border-color var(--dur-fast) var(--ease);
}
@media (hover:hover) and (pointer:fine){
  .nowrow-check:hover::before{ border-color:var(--ink); }
}
.nowrow-check:focus-visible{ box-shadow:var(--focus-ring); outline:none; }
.nowrow-go{
  flex:1; min-width:0; min-height:60px;
  display:flex; align-items:center; gap:var(--space-3);
  padding:var(--space-2) 0;
  background:none; border:0; font:inherit; color:inherit; text-align:left;
}
button.nowrow-go{ cursor:pointer; }
button.nowrow-go:focus-visible{ box-shadow:var(--focus-ring); outline:none; }
.nowrow-body{ flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.nowrow-title{
  font-size:var(--fs-3); font-weight:var(--fw-body); color:var(--ink);
  line-height:1.35; overflow-wrap:anywhere;
}
.nowrow-meta{ font-size:var(--fs-2); color:var(--ink-mute); line-height:1.45; }
.nowrow-dur{ flex:0 0 auto; font-size:var(--fs-3); color:var(--ink-mute); }

/* Suggested to do now and Coming next, decision D32. Two regions in one
   treatment: the first holds the one row that starts a session, a step larger
   and carrying the play mark that says so; the second is read, as the rows
   behind the head always were. The mark is outlined in ink, never filled, so
   Sail now stays the only filled thing on the screen. */
.nowq + .nowq{ margin-top:var(--space-5); }
.nowq--now .nowrow{ border-bottom-color:var(--line-strong); }
.nowq--now .nowrow-title{ font-size:var(--fs-4); font-weight:var(--fw-med); }
.nowrow-play{
  flex:0 0 auto; width:34px; height:34px; margin-left:var(--space-2);
  display:grid; place-items:center;
  border:1px solid var(--line-strong); border-radius:var(--r-pill); color:var(--ink);
}
.nowrow-play svg{ width:10px; height:12px; }

/* Decision D41: the one to do now leads Now and carries the screen's one
   band, the accent as an edge on the live row, which is the one border
   tokens.css rule 2 allows it. So it carries .is-now, the live band's own
   class, rather than a second name for the same thing: the one to do now is
   the next action, and a band on anything else is still decoration. Not now
   sits small under it, an underline rather than a box, the way the other
   quiet actions on this screen are. */
.nowsug{ margin:0 0 var(--space-5); }
.nowsug.is-now .nowrow{ border-left:var(--band-w) solid var(--accent); padding-left:var(--space-2); }
.nowsug .nowrow-check{ margin-left:0; }
.nowmove.btn--tertiary{
  display:flex; width:auto; margin:var(--space-2) 0 0 auto; min-height:32px;
  padding-left:0; padding-right:0; font-size:var(--fs-2);
  border-color:transparent; border-bottom-color:var(--line); border-radius:0;
}


/* =========================================================================
   THE TASK LIST, AS A LIST.

   It was a stack of outlined, filled, 3px-radius rectangles with 8px between
   them: twenty two floating boxes down a page whose entire design rule is that
   content does not float. Every row carried a bucket button and a delete
   button permanently, plus three priority pips and a middle-dot run of five
   facts, at which point a task is a database record rather than a thing to do.

   A list separated by hairlines is a list. The row keeps every control it had.
   ========================================================================= */

.tasklist{ display:flex; flex-direction:column; gap:0; }

.taskrow{
  background:none;
  border:none;
  border-top:1px solid var(--line-soft);
  border-radius:0;
  padding:var(--space-4) 0;
  display:flex; align-items:flex-start; gap:var(--space-3);
  animation:rise var(--dur-base) var(--ease) both;
}
.tasklist > .taskrow:first-child{ border-top:none; }

/* Priority, as one mark rather than three dots.
   Three 6px circles stacked vertically beside every row is a legend the reader
   has to learn and then decode on every line. One rule whose weight is the
   priority is read without decoding. */
.prio{ display:flex; flex-direction:column; gap:0; flex-shrink:0;
  width:var(--band-w); align-self:stretch; min-height:34px; margin-top:2px; }
.prio i{ width:100%; height:auto; flex:1; border-radius:0; background:none; }
.prio.p1 i:nth-child(1), .prio.p2 i:nth-child(-n+2), .prio.p3 i{ background:var(--line-strong); }
.prio.p3 i{ background:var(--ink-soft); }

.taskrow .nm{ white-space:normal; overflow:visible; text-overflow:clip;
  font-size:var(--fs-4); font-weight:var(--fw-med); line-height:1.3; }
.taskrow .sub{ line-height:1.5; }

/* The row's own controls, quiet until approached.
   .row-destroy stays permanently visible and is asserted to be: hiding it
   behind a long press once made deleting undiscoverable. The bucket control
   is a different thing, used far less, and it does not need to shout on every
   one of twenty two rows. It stays in the tab order and stays visible to
   anything without a hover, so nothing becomes unreachable. */
.row-bucket{
  flex-shrink:0; font-size:var(--fs-1); font-weight:var(--fw-med);
  color:var(--ink-mute); background:none; border:none;
  border-bottom:1px solid var(--line-soft);
  padding:var(--space-1) 0;
  transition:color var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
.row-bucket:focus-visible{ box-shadow:var(--focus-ring); outline:none; }

/* Both destinations, because there are only ever two.
 *
 * The row carried one button that cycled now -> later -> oneday -> now, so
 * getting something out of One day and back into Now meant passing through
 * Later: two taps, and the first of them saved a state nobody chose and
 * announced it. Three buckets have exactly two destinations each and neither
 * has any reason to be further away than the other.
 *
 * Side by side rather than stacked. Stacked, two 18px targets four pixels
 * apart is a mis-tap between "Later" and "One day" waiting to happen, and a
 * mis-tap here moves somebody's work somewhere they did not ask for. They wrap
 * onto two lines when the row is too narrow for both, which on a 360px phone
 * with a long task name is where they end up. */
.row-buckets{
  display:flex; flex-wrap:wrap; justify-content:flex-end;
  gap:var(--space-1) var(--space-3);
  flex-shrink:0; margin-top:var(--space-1);
}
@media (hover:hover) and (pointer:fine){
  .row-bucket{ opacity:0; }
  .taskrow:hover .row-bucket,
  .taskrow:focus-within .row-bucket{ opacity:1; }
  .row-bucket:hover{ color:var(--ink); border-bottom-color:var(--line-strong); }
}

/* The folds. A summary is a control, so it looks like one, and the count
   belongs to it rather than being buried in the label. */
.taskrest{ border-top:1px solid var(--line-soft); }
.taskrest > summary{
  cursor:pointer; list-style:none;
  padding:var(--space-3) 0;
  font-size:var(--fs-2); font-weight:var(--fw-med); color:var(--ink-soft);
  display:flex; align-items:baseline; gap:var(--space-2);
}
  /* Two markers, one control. list-style and the WebKit pseudo-element cover
     old Safari; ::marker is what Chrome actually draws, and without it the
     summary carried the browser's triangle next to the chevron this rule
     provides. */
.taskrest > summary::-webkit-details-marker{ display:none; }
.taskrest > summary::marker{ content:""; }
.taskrest > summary::after{
  content:""; width:6px; height:6px; margin-left:auto; align-self:center;
  border-right:1.5px solid var(--ink-mute); border-bottom:1.5px solid var(--ink-mute);
  transform:rotate(45deg); transition:transform var(--dur-fast) var(--ease);
}
.taskrest[open] > summary::after{ transform:rotate(-135deg); }
.taskrest > summary em{ font-style:normal; color:var(--ink-mute); font-weight:var(--fw-body); }
.taskrest > summary:focus-visible{ box-shadow:var(--focus-ring); outline:none; }

/* Asking Sailyn for a plan, beside the list rather than under it. */
.ask .fnote{ margin-top:var(--space-2); }

/* =========================================================================
   THE ROW, EVERYWHERE ELSE.

   .row is the shared list primitive and it had no separator of its own beyond
   `.row + .row`, which is right. What it did not have was a resting state that
   reads as a line in a list rather than as free-floating text. */

.upnext-row:first-child{ border-top:none; }


/* =========================================================================
   INSIGHTS AND STORY, COMPOSED.
   ========================================================================= */

/* The sentence under a lead's numbers. Not a heading and not metadata: the one
   line that says what the numbers mean. */
.lead-line{
  font-size: var(--fs-3); color: var(--ink-soft); line-height: 1.55;
  max-width: 62ch; font-weight: var(--fw-med);
}

/* The scene is the lead on Story, so it keeps its own spacing rather than the
   type rhythm the other leads use. */
.lead--scene{ display: flex; flex-direction: column; gap: var(--space-4); }
.lead--scene .lead-line{ margin: 0; }

/* A heading inside a region, for a list that belongs to the region's subject
   rather than being a subject of its own. One step down from .region-title,
   and it is not a section head: a bare .section-h nested inside a region was
   the same size as the region's own heading and read as a sibling. */
.subhead{
  font-size: var(--fs-2); font-weight: var(--fw-hi); color: var(--ink-mute);
  letter-spacing: var(--track-label); text-transform: uppercase;
  margin: var(--space-5) 0 var(--space-3);
}

/* The wallet.
   It was a tile in a grid of five facts, identical in shape to the four that
   were not pressable, with "tap to spend" underneath doing the work the shape
   should have done. It is a row with a named action now. */
.wallet{ align-items: baseline; gap: var(--space-3); }
.wallet-n{
  display: block;
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-5); font-weight: var(--fw-med); color: var(--gold);
  line-height: 1.1; letter-spacing: -0.02em;
}
.wallet-go{
  font-size: var(--fs-2); font-weight: var(--fw-med); color: var(--ink-soft);
  border-bottom: 1px solid var(--line); white-space: nowrap; flex-shrink: 0;
}
@media (hover:hover) and (pointer:fine){
  .wallet:hover .wallet-go{ color: var(--ink); border-bottom-color: var(--line-strong); }
}
.wallet:focus-visible{ box-shadow: var(--focus-ring); outline: none; }

/* The unit on a measured number rides at the size of the label, not of the
   number, or "12h" reads as two numbers. */
.metric-n small{ font-size: var(--fs-2); font-weight: var(--fw-med); color: var(--ink-mute); margin-left: 1px; }

/* Bullets, neutral. */
.fdot{ background: var(--line-strong); }
.upnext .pdot{ background: var(--line-strong); }

/* Progress's segments, while it holds Story, Insights and Challenges. Task
   2.3, behind flags.navFour. UI-SPEC section 7's geometry: the full measure,
   36px tall, a pill with a hairline, equal segments, the active one an inset
   pill. Not its colour: section 7 puts the active segment in --accent, which
   is conflict 2 in section 12 and still open, so the active one is said in ink
   and weight, the way the tab bar says it. A control keeps its box. */
.progseg{
  display: flex; width: 100%; height: 36px; padding: 2px;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--surface-solid);
  margin: var(--space-4) 0 var(--space-5);
}
.progseg-b{
  flex: 1; min-width: 0; border: 1px solid transparent; border-radius: var(--r-pill);
  font-size: var(--fs-2); font-weight: var(--fw-med); color: var(--ink-mute);
  white-space: nowrap;
}
.progseg-b.on{ border-color: var(--line-strong); color: var(--ink); font-weight: var(--fw-hi); }
.progseg-b:focus-visible{ box-shadow: var(--focus-ring); outline: none; }


/* =========================================================================
   THE RAIL AND THE HEADER.

   The rail is --surface-2, the one genuinely different plane in this system,
   and it is the left edge of the product. Its items are rules, not boxes: a
   hairline between them, the current one marked by a band on its leading edge
   and by weight, which is the treatment the rail comment has described since
   the token swap and which a primitive four hundred lines away was overriding.
   ========================================================================= */

@media (min-width: 880px){
  /* The header sits in the rail, so it is part of the same plane and needs a
     rule under it rather than a change of colour. */
  #app header{
    border-bottom: 1px solid var(--line);
    padding-bottom: var(--space-4);
  }
  #app header .hdr-right{ gap: var(--space-1); }

  /* The architecture, made visible.
     Now is what to do; Plan and Tasks are what is coming; Insights is where it
     is heading; Story and Challenges are what has happened. Three groups, and
     a rule that steps up between them says so without a word of copy.
     With four tabs (Task 2.3) Insights, Story and Challenges are Progress, so
     the groups are Now, then Plan and Tasks, then Progress. */
  #app nav button[data-tab="plan"],
  #app nav button[data-tab="insights"],
  #app nav button[data-tab="story"],
  #app nav button[data-tab="progress"]{ border-top-color: var(--line); }
  #app nav button[data-tab="plan"],
  #app nav button[data-tab="insights"],
  #app nav button[data-tab="story"],
  #app nav button[data-tab="progress"]{ margin-top: var(--space-2); padding-top: var(--space-4); }

  #app nav button{ color: var(--ink-soft); }
  #app nav button svg{ color: var(--ink-mute); }
  #app nav button.on svg{ color: var(--accent); }
}

/* The header controls are one family, so they are one size and one shape.
   They were a 7px dot, a 24px mail glyph, a pill carrying a number in gold,
   and two 38px circles, in a row, which is five shapes for five controls. */
.mailbtn, .gear, .themebtn{
  width: 34px; height: 34px; border-radius: var(--r-control);
  display: grid; place-items: center;
}
.mailbtn svg, .gear svg, .themebtn svg{ width: 18px; height: 18px; }
/* The balance, decision D30: the family's height and shape with a number in
   it, so it is the one control wider than it is tall. Gold, because it is what
   has been earned, and the number in the numeric face like every count. */
.hdrcoins{
  height: 34px; padding: 0 var(--space-2); border: 0; background: none;
  border-radius: var(--r-control); cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--gold); font-size: var(--fs-2); font-weight: var(--fw-med);
}
.hdrcoins svg{ width: 16px; height: 16px; }
.hdrcoins-n{ font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.hdrcoins:focus-visible{ box-shadow: var(--focus-ring); outline: none; }
/* display:grid above (and, for language and theme, in settings.css) outranks
   the browser's own [hidden], so every control the frame shows and hides by
   screen says it again here. Before Task 2.4 this named only the gear and the
   avatar; mailbtn had the same display:grid and no override, so the Notices
   icon could never actually disappear behind its own hidden attribute. */
.gear[hidden], .hdrcoins[hidden], .mailbtn[hidden], .langbtn[hidden], .themebtn[hidden]{ display: none; }

/* =========================================================================
   RESPONSIVE.

   The phone reads the two columns in order and the context column takes a rule
   above it, so the shape of the day arrives after the work rather than before
   it. The one thing that must not happen at any width is a page that scrolls
   sideways, so anything that can be wider than its column says so.
   ========================================================================= */

.page, .page-main, .page-side, .region{ min-width: 0; }
.timefield, .plan, .metrics, .skills, .outlooks{ max-width: 100%; }
/* Wide content scrolls inside itself rather than pushing the page. */
.rv-spark, .rhythm{ overflow-x: auto; }

@media (max-width: 879px){
  /* On a phone the head is two lines, not two columns: a 40 character
     greeting and an action side by side leaves neither enough room. */
  .nowhead{ flex-direction: column; align-items: stretch; gap: var(--space-3); }
  .nowhead-acts{ justify-content: flex-start; }
  .region-head{ flex-wrap: wrap; }
}

@media (max-width: 420px){
  /* The type scale steps down at the top only. A 40px title on a 360px screen
     takes three lines before the work is even named. */
  .lead-title{ font-size: var(--fs-5); }
  #app main{ padding-left: var(--space-4); padding-right: var(--space-4); }
}

/* A folding phone's cover screen, decision D48. It is 260 to 300 wide, and
   the header's wordmark, balance and three controls needed nearer 300: the
   gear went off the edge. Below 340 the wordmark keeps its icon and drops
   the word, the header takes the column's own 16px sides, and the controls
   close up. The icon is still the brand, so nothing is lost. */
@media (max-width: 340px){
  #app header{ padding-left: var(--space-4); padding-right: var(--space-4); }
  #app header .brand span{ display: none; }
  #app header .hdr-right{ gap: var(--space-1); }
  .hdrcoins{ padding: 0 var(--space-1); }
}


/* =========================================================================
   THE TIMETABLE, IN A MEASURE.
   ========================================================================= */

/* Two columns of days was right when the screen was one 760px column. Inside
   the primary column of a split page it is two 250px columns, and a task name
   in 250px is an ellipsis.

   minmax(0, 1fr), never a bare 1fr: a bare one may not shrink below the
   widest thing in it, and a fixed session's name is one line that ellipsizes,
   so a long calendar title made the whole fortnight as wide as that title and
   ran it under the Goals column whenever the window was not full screen
   (founder report, 11 October). */
.page--split .plan{ grid-template-columns: minmax(0, 1fr); }
.pday{ min-width: 0; }

/* A day's work, on two lines like every other list in the product: what it is
   and when on the first line with the tick and the cross at its end, the
   facts under the name. A grid, because a wrapping row put the dot on a line
   of its own and split the free space between the tick and the cross, which
   left the tick floating in the middle of the day. */
.pd-task{ display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto;
  column-gap: var(--space-2); row-gap: 2px; align-items: start; padding: var(--space-2) 0; }
.pd-task > .pd-clock, .pd-task > .pdot, .pd-task > .pbox{ grid-column: 1; grid-row: 1; }
.pd-nm{
  grid-column: 2; grid-row: 1; min-width: 0;
  white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; line-height: 1.35;
}
.pd-clock{ line-height: 1.6; }
.pd-task .pdot{ margin-top: 8px; }
.pd-meta{
  grid-column: 2 / -1; grid-row: 2; min-width: 0;
  font-size: var(--fs-2); line-height: 1.45;
}
.pd-done{ grid-column: 3; grid-row: 1; }
.pd-del{ grid-column: 4; grid-row: 1; }
.pd-fix{ min-width: 0; }

/* A bar you can read, here too. Track is the hairline, fill is ink. */
.pd-bar{ background: var(--line-soft); }
.pd-bar i{ background: var(--ink-soft); }

/* An open day is capacity, not a gap in the record.
   It was drawn at 55% opacity with the word "clear" beside it, which is how
   you draw something that is missing. A day with nothing on it is the most
   valuable thing this product can show you. */
.pday.empty{
  opacity: 1;
  color: var(--ink-mute);
  border-top: 1px dashed var(--line);
  padding-top: var(--space-3);
}
.pday.empty .pd-date b{ color: var(--ink-mute); font-weight: var(--fw-med); }

/* Moving the fortnight (19 September). Earlier and later either side of the
   days on screen, and a date to go straight to, under a hairline like every
   other region edge. The two arrows are controls, so they keep a box. */
.pwin{ display: flex; flex-direction: column; gap: var(--space-2);
  padding-bottom: var(--space-3); margin-bottom: var(--space-3); border-bottom: 1px solid var(--line); }
.pwin-row{ display: flex; align-items: center; gap: var(--space-2); }
.pwin-range{ flex: 1 1 auto; min-width: 0; text-align: center;
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-3); font-weight: var(--fw-med); color: var(--ink); }
/* The shared tertiary button, held square: 44px is the product's smallest
   target. */
.pwin .pwin-step{ flex: 0 0 auto; width: 44px; min-height: 44px; padding: 0; }
.pwin-step svg{ width: 18px; height: 18px; }
.pwin-step:focus-visible{ box-shadow: var(--focus-ring); outline: none; }
.pwin-row--go{ flex-wrap: wrap; justify-content: space-between; }
.pwin-go{ display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
  font-size: var(--fs-2); color: var(--ink-soft); }
.pwin-date{ width: auto; min-height: 36px; font-size: var(--fs-3); }
.pwin-today{ width: auto; min-height: 36px; }
/* A finished task on a day that has gone: greyed, the filled check, never
   struck through, the same row Plan draws for work finished today. */
.pd-task.is-done .pd-nm{ color: var(--ink-mute); }

/* =========================================================================
   THE LAST OF THE OUTLINED BLOCKS.

   Every one of these was a --surface fill, a --line border on all four sides
   and a control radius, which on a ground that --surface resolves to is a box
   drawn around content and nothing else. Separation is a hairline on one edge.
   ========================================================================= */

.boost, .outlook, .mile, .stage, .challenge, .board-row, .goal, .goal-empty,
.tcard, .home, .statbox, .insight, .revcard, .forecast{
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding-left: 0;
  padding-right: 0;
}
.boost, .outlook, .mile, .goal{ border-top: 1px solid var(--line-soft); padding-top: var(--space-3); }
.boosts > .boost:first-child,
.outlooks > .outlook:first-child,
.miles > .mile:first-child,
.goals > .goal:first-child{ border-top: none; padding-top: 0; }
.outlook.warn{ background: none; }
/* The state has to be said in something other than a fill, now that there is
   no fill: the leading edge carries it. */
.outlook.warn{ border-left: var(--band-w) solid var(--warn); padding-left: var(--space-3); }
.outlook.ok{ padding-left: calc(var(--band-w) + var(--space-3)); }

.boosts, .outlooks, .miles{ gap: var(--space-3); }


/* The current tab on a phone, said the way the rail says it.
   It was a filled --surface-2 pill, three lines below a rule stating that the
   active tab carries no fill. A bar of six items with one of them in a box is
   the card language surviving in the last place it had left to hide. The mark
   is a band on the leading edge, which is the same sentence the rail speaks,
   plus the icon and the weight. */
@media (max-width: 879px){
#app nav button.on{ background:none; box-shadow:inset 0 2px 0 var(--accent); }
#app nav button.on span{ font-weight:var(--fw-hi); color:var(--ink); }
}

/* Measured numbers in the context column line up in a grid rather than
   wrapping into a ragged run. Three across a 360px column is two and a half. */
.page-side .metrics{
  display:grid; grid-template-columns:repeat(2, minmax(0,1fr));
  gap:var(--space-5) var(--space-4);
}
.metric-n small{ margin-left:2px; }


/* =========================================================================
   CHALLENGES.

   The last of the outlined cards. A challenge is a row in a list under a
   heading that says what state it is in, which is the question somebody opens
   this screen to answer.
   ========================================================================= */

.ch-menu{ gap: 0; }
.ch-card{
  background: none;
  border: none;
  border-top: 1px solid var(--line-soft);
  border-radius: 0;
  padding: var(--space-4) 0;
  align-items: baseline;
}
.ch-menu > .ch-card:first-child{ border-top: none; }
.ch-card-nm{ font-size: var(--fs-4); font-weight: var(--fw-med); white-space: normal; overflow: visible; text-overflow: clip; }
.ch-card-sub{ line-height: 1.45; }
/* "in" is a state, so it is a word in the metadata rather than a bordered
   capsule floating beside the name. */
.ch-card-in{
  border: none; border-radius: 0; padding: 0;
  color: var(--ink-soft); font-weight: var(--fw-hi);
  letter-spacing: var(--track-label); text-transform: uppercase;
}
.ch-card-go{ border-bottom: 1px solid var(--line); align-self: baseline; }
@media (hover:hover) and (pointer:fine){
  .ch-card:hover .ch-card-go{ color: var(--ink); border-bottom-color: var(--line-strong); }
}
.ch-card:focus-visible{ box-shadow: var(--focus-ring); outline: none; }


/* =========================================================================
   THE LAST INVISIBLE MARKS.

   A chart drawn in --surface-2 on a ground that --surface resolves to is a
   0.025 step of lightness: a bar at zero and a bar at maximum are the same
   picture. Four separate charts were drawn that way, including the weekly
   spark, which is the one thing on Insights that is supposed to be read at a
   glance. Where a mark carries a quantity it is set in ink, which spends no
   colour and is legible at both ends of the range.
   ========================================================================= */

.review{
  background: none; border: none; border-radius: 0;
  padding: 0; margin-bottom: 0;
}
.rv-fill{ background: var(--ink-soft); opacity: 1; }
.rv-col:last-child .rv-fill{ background: var(--ink); }
/* The window the record actually points at, and the rest of the day around it.
   The quiet hours are still drawn: a peak with nothing beside it is a claim
   rather than a reading, and the shape of the day is half of what the strip is
   for. The peak is the only one at full weight, which is the same way the four
   bars this replaced marked their winner. */
.rbar{ background: var(--ink-soft); opacity: .4; }
.rbar.is-peak{ background: var(--ink); opacity: 1; }
.pd-bar i{ background: var(--ink-soft); }
.blockbar .bar i{ background: var(--ink-soft); }
.cap-bar.tight i{ background: var(--warn); }

/* A bar is a rule with a fill, not a boxed control. */
.bar{ height: 6px; background: var(--line-soft); border: none; }
.bar i{ background: var(--ink-soft); }
.skill .bar i, .score .bar i{ background: var(--ink-soft); }


/* =========================================================================
   SHEETS.

   The one place in this product where a real shadow is permitted, because a
   sheet genuinely floats above the app rather than sitting on the page. What
   it did not have was an anatomy: a floated close button overlapping a
   heading, a drag handle on a desktop dialog nobody can drag, and a body that
   ran straight into the actions at the bottom with nothing marking either end.
   ========================================================================= */

.sheet{ padding-top: var(--space-3); }

/* The close control sits in the corner and stays there while the body
   scrolls. Floated, it was in the text flow of whatever came first, so a sheet
   whose first element was a heading got a heading indented around a circle. */
.sheet-x{
  float: none;
  position: absolute; top: var(--space-3); right: var(--space-4);
  margin: 0;
  border-radius: var(--r-control);
  background: none; border: 1px solid transparent;
  z-index: 3;
}
/* No `position: relative` here.
   An earlier pass added one so the absolutely positioned close control had a
   containing block. .sheet is already `position: fixed`, which is a containing
   block for its absolutely positioned descendants, and declaring `relative`
   later in the file overrode the `fixed` the sheet depends on: the panel left
   the viewport, fell into normal flow at the end of the document, and opened
   in the corner of the page instead of over it. */
.sheet > h3, .sheet > h2{ padding-right: calc(34px + var(--space-3)); }

/* A rule under the title, so the sheet has a head and a body rather than one
   undifferentiated column. */
.sheet > h3{
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-4);
}

@media (min-width: 880px){
  /* A grab handle on a centred dialog is an affordance for a gesture that does
     not exist here. It stays on the phone, where the sheet really does come up
     from the bottom edge. */
  .sheet .grab{ display: none; }
  .sheet-x{ top: var(--space-4); right: var(--space-4); }
}

/* Fields, as a form rather than as a stack of boxes.
   The label carries the naming and the input carries the affordance, which is
   the one place in this system a box is correct. */
.field label{
  letter-spacing: var(--track-label); text-transform: uppercase;
  font-size: var(--fs-1); color: var(--ink-mute);
}
.field input:not([type=checkbox]):not([type=range]), .field select{
  font-size: var(--fs-3);
  transition: border-color var(--dur-fast) var(--ease);
}
.field input:not([type=checkbox]):not([type=range]):hover,
.field select:hover{ border-color: var(--line-strong); }
.field input:focus-visible, .field select:focus-visible{
  border-color: var(--ink-soft);
  box-shadow: var(--focus-ring);
}

/* The settings menu, as rows under a rule rather than as a run of panels. */
.setmenu-row{ border-radius: 0; }

/* The share card's preview, B2. The card is drawn first and shown here, and
   Share under it is the tap the share sheet needs, so the picture is never
   drawn after the tap has run out. Sized to the sheet and never taller than
   about half the screen, because the button under it has to stay in reach on
   a phone. No frame and no shadow: the picture is the content. */
.shc-fig{ margin: 0 0 var(--space-4); min-height: 120px;
  display: flex; align-items: center; justify-content: center; }
.shc-fig img{ display: block; max-width: 100%; max-height: 52dvh; width: auto; height: auto; }
.shc-text{ margin: 0; font-size: var(--fs-3); color: var(--ink); }
/* The rows under the card, M4: what is on it is what is ticked. A list of
   plain checkboxes, because each row is one yes or no and nothing more. */
.shc-rows{ border: 0; margin: 0 0 var(--space-4); padding: 0; display: grid; gap: var(--space-2); }
.shc-rows legend{ padding: 0; margin-bottom: var(--space-2); font-size: var(--fs-1);
  letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink-mute); }
.shc-row{ display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-3); color: var(--ink); }
.shc-row input{ width: 18px; height: 18px; margin: 0; }
/* A checkbox row inside a field is an answer, not the field's name. The label
   rule above outranked .shc-row and set "Count this calendar" in small capitals
   with the box against the first letter. */
.field label.shc-row{ display: flex; text-transform: none; letter-spacing: normal;
  font-size: var(--fs-3); font-weight: var(--fw-body); color: var(--ink); margin-bottom: 0; }
/* C6, a calendar file's preview: the box beside a name, its times and any note. */
.ics-row{ align-items: flex-start; }
.ics-row input{ margin-top: 2px; flex: none; }
.ics-row > span{ display: grid; gap: 2px; min-width: 0; }
/* C8, a capture key shown once: it has to be read and copied whole. */
/* The capture key's steps: a numbered list that reads like the rest of a note. */
.capture-h{ font-weight: var(--fw-med); color: var(--ink); margin-top: var(--space-3); }
.capture-steps{ margin: var(--space-2) 0; padding-left: var(--space-5); display: grid; gap: var(--space-1); font-size: var(--fs-2); color: var(--ink-mute); }
.capture-key{ display: block; margin-block: var(--space-2); font-family: var(--font-num); font-size: var(--fs-2); word-break: break-all; }


/* =========================================================================
   THE SESSION.

   The calmest surface in the product, and the one place --fs-numeric is spent.
   Nothing here changed except the two things that were not visible: the ring
   the countdown runs around, and the day ring inside it, were both stroked in
   --surface, which resolves to the ground. So the clock ran around nothing,
   and at the start of a session the only mark on screen was the round cap of a
   progress arc with no length yet, which reads as a dot in an empty field.
   ========================================================================= */

.ring-bg{ stroke: var(--line); }
.ring-bg.day{ stroke: var(--line-soft); }


/* The actions belonging to a region's content rather than to its heading:
   more than one, so they cannot sit on the heading line, and still not
   important enough to be full-width blocks under the thing they act on. */
.region-acts{ display:flex; flex-wrap:wrap; gap:var(--space-2) var(--space-5); margin-top:var(--space-4); }
.region-acts .btn--tertiary{
  border-color:transparent; border-bottom-color:var(--line);
  border-radius:0; padding-left:0; padding-right:0; min-height:32px;
}
@media (hover:hover) and (pointer:fine){
  .region-acts .btn--tertiary:hover{ color:var(--ink); border-bottom-color:var(--line-strong); }
}


/* A running session, minimised (session/mini.js, 5 October). The control on
   the session screen is chrome, a hairline-edged control in the corner, and
   the strip is the tab bar's material directly above it: solid ground, one
   rule along its top edge, no shadow and no accent. The clock is the strip's
   one number, in the numeric face. */
.sess-min{
  /* In the flow at the top, to the right, so it never sits over the sound
     controls under it. */
  align-self:flex-end; flex:0 0 auto; margin:calc(var(--space-4) * -1) 0 var(--space-3);
  display:inline-flex; align-items:center; gap:var(--space-1);
  min-height:36px; padding:0 var(--space-3);
  background:none; border:1px solid var(--line); border-radius:var(--r-control);
  color:var(--ink-soft); font-size:var(--fs-2); font-weight:var(--fw-med); cursor:pointer;
}
.sess-min svg{ width:16px; height:16px; }
.sessmini{
  position:fixed; z-index:var(--z-nav); left:0; right:0; margin:0 auto; max-width:520px;
  bottom:calc(var(--nav-h) + max(var(--space-2), var(--safe-bottom)));
  display:flex; align-items:center; gap:var(--space-3);
  padding:var(--space-2) var(--space-4);
  background:var(--surface-solid); border-top:1px solid var(--line);
}
.sessmini-open{
  flex:1 1 auto; min-width:0; display:flex; flex-direction:column; align-items:flex-start; gap:2px;
  background:none; border:0; padding:var(--space-1) 0; text-align:left; color:var(--ink); cursor:pointer;
}
.sessmini-name{
  max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:var(--fs-2); color:var(--ink-soft);
}
.sessmini-time{ font-family:var(--font-num); font-variant-numeric:tabular-nums; font-size:var(--fs-4); font-weight:var(--fw-med); }
.sessmini-acts{ display:flex; gap:var(--space-2); flex:0 0 auto; }
.sessmini-acts .btn{ margin:0; min-height:40px; padding:0 var(--space-3); width:auto; }
/* Other audio took the lock screen: one line and the button that puts
   Sailyn back (session/lockcard.js), on the session and above its strip. */
.lockback{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2) var(--space-3); margin:0 0 var(--space-3); }
.lockback-line{ margin:0; flex:1 1 14em; font-size:var(--fs-2); color:var(--ink-soft); }
.lockback .btn{ margin:0; width:auto; min-height:40px; padding:0 var(--space-3); }
.sessmini{ flex-wrap:wrap; }
.sessmini .lockback{ order:-1; flex:1 0 100%; margin:0; }
/* Room for it: the last row of a screen and the plus clear the strip. */
body[data-sessmini] #app main{
  padding-bottom:calc(var(--nav-h) + max(var(--space-2), var(--safe-bottom)) + var(--space-4) + 64px);
}
body[data-sessmini] .pfab{ bottom:calc(var(--nav-h) + max(var(--space-2), var(--safe-bottom)) + 19px + 64px); }
/* From 880px the tabs are a rail on the left, so the strip sits in the
   bottom corner of the work, edged by the one hairline ring. */
@media (min-width: 880px){
  .sessmini{
    left:auto; right:var(--space-5); bottom:var(--space-5); width:calc(100% - 2 * var(--space-5)); max-width:26.25rem;
    border-top:0; box-shadow:var(--shadow-1); border-radius:var(--r-control);
  }
  body[data-sessmini] #app main{ padding-bottom:calc(var(--space-6) + 72px); }
}

/* What the session holds shut (D130): small marks under the clock, quiet
   enough to read past. Pills with a hairline, never a fill. */
.sess-shut{ display:flex; flex-direction:column; align-items:center; gap:var(--space-2); margin:0 0 var(--space-4); }
.sess-shut-l{ margin:0; font-size:var(--fs-2); color:var(--ink-mute); }
/* A focus or break nudge, said on the screen while it is open (§2). */
.sess-nudge{ margin:0 0 var(--space-3); max-width:32ch; font-size:var(--fs-2); color:var(--ink-mute); text-align:center; }
.sess-step{ display:flex; flex-direction:column; align-items:center; gap:var(--space-1); margin:0 0 var(--space-3); max-width:34ch; text-align:center; }
.sess-step-l{ font-size:var(--fs-1); letter-spacing:var(--track-label); text-transform:uppercase; color:var(--ink-mute); }
.sess-step-t{ font-size:var(--fs-3); font-weight:var(--fw-med); }
.nudge-num{ width:6ch; margin-inline-start:var(--space-2); font-family:var(--font-num); }
.sess-shut-list{ display:flex; flex-wrap:wrap; justify-content:center; gap:var(--space-2); list-style:none; margin:0; padding:0; }
.sess-shut-list li{ display:inline-flex; align-items:center; gap:var(--space-1); padding:2px var(--space-3);
  border:1px solid var(--line); border-radius:var(--r-pill); font-size:var(--fs-1); color:var(--ink-soft); }
.sess-shut-more .num{ font-family:var(--font-num); font-variant-numeric:tabular-nums; }
/* =========================================================================
   THE OVERLAY SYSTEM.

   Everything that floats over the app: sheets, dialogs, the session takeover,
   the undo bar, toasts. Four rules, applied from one place rather than as a
   per-screen offset each time somebody notices a control has gone under
   something.

   1. Nothing that floats may be taller than the usable screen.
      --overlay-max-h is the viewport minus both insets minus a step, so the
      top edge of a centred dialog cannot land under a Dynamic Island. .onboard
      was max-height:88dvh centred on the viewport, which puts its top edge at
      6% of the screen: on a 6.7 inch iPhone that is underneath the Island, and
      the close control is at the top of the dialog.

   2. A control that dismisses or confirms is never in the scrolling part.
      The Add a Task sheet had Save as the last element of a scrolling body, so
      on a phone with the keyboard up it was below the fold of a panel that was
      itself below the fold. Header and footer are pinned; only the middle
      scrolls.

   3. Every fixed edge clears its inset. Bottom controls clear the home
      indicator, top controls clear the notch, and horizontal padding clears
      whichever side the notch is on in landscape.

   4. One scroll container per overlay, with overscroll contained, so dragging
      at the end of a sheet does not scroll the page behind it.
   ========================================================================= */

/* ---- 1. nothing outgrows the usable screen ---- */

.onboard{
  max-height: var(--overlay-max-h);
  /* Centred between the insets rather than on the raw viewport. With a notch
     at the top and a home indicator at the bottom those are different numbers,
     and centring on the viewport puts the difference into the top edge. */
  top: calc(50% + (var(--safe-top) - var(--safe-bottom)) / 2);
}
.session{
  /* dvh, and both insets. The session is the one screen you cannot leave by
     any other route, so Pause and Finish being reachable is not a nicety. */
  max-height: 100svh;
  padding-left: max(var(--space-5), var(--safe-left));
  padding-right: max(var(--space-5), var(--safe-right));
}

/* ---- 2. the framed sheet: pinned head, scrolling body, pinned foot ---- */

.sheet--framed{
  display: flex; flex-direction: column;
  /* The panel itself no longer scrolls; its body does. */
  overflow: hidden;
  max-height: min(88svh, calc(100svh - var(--safe-top) - var(--space-5)));
  padding-bottom: 0;
}
.sheet--framed > .sheet-head{
  flex: 0 0 auto;
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
  margin-bottom: 0;
}
.sheet--framed > .sheet-head h3{
  margin: 0; padding: 0; border: 0;
  font-size: var(--fs-5); font-weight: var(--fw-med); letter-spacing: var(--track-title);
}
.sheet--framed > .sheet-body{
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  padding-block: var(--space-4);
}
.sheet--framed > .sheet-body::-webkit-scrollbar{ display: none; }
/* The actions. Pinned to the bottom of the panel, above the home indicator,
   with a rule so it reads as the end of the form rather than as the next
   field. */
.sheet--framed > .sheet-foot{
  flex: 0 0 auto;
  display: flex; flex-direction: column; gap: var(--space-2);
  padding-top: var(--space-4);
  padding-bottom: calc(var(--safe-bottom) + var(--space-4));
  border-top: 1px solid var(--line);
  background: var(--surface);
}
@media (min-width: 880px){
  .sheet--framed{
    max-height: var(--overlay-max-h);
  }
  .sheet--framed > .sheet-foot{ padding-bottom: var(--space-4); }
  /* Two ranks side by side once there is room for them. */
  .sheet--framed > .sheet-foot{ flex-direction: row-reverse; align-items: center; }
  .sheet--framed > .sheet-foot > .btn{ flex: 1 1 auto; }
}

/* The close control sits in the head's own row on a framed sheet, so it can
   never overlap the title however long the title is. */
.sheet--framed > .sheet-head .sheet-x{ position: static; flex: 0 0 auto; }

/* ---- 3. every fixed edge clears its inset ---- */

.scrim{
  /* Painted under the notch on purpose: a dimmed screen that stops short of
     the corners reads as a panel rather than as the app being covered. */
  padding: 0;
}
.sheet{
  /* Horizontal insets for landscape. The sheet is capped at 520px and centred,
     so this only bites on a phone on its side, which is exactly when a notch
     is on one of these edges. */
  padding-left: max(var(--space-5), var(--safe-left));
  padding-right: max(var(--space-5), var(--safe-right));
}
.toast{
  left: 50%;
  max-width: min(88vw, calc(100vw - var(--safe-left) - var(--safe-right) - var(--space-6)));
}

/* ---- 4. one scroll container, contained ---- */
.sheet, .onboard, .session, .scrim{ overscroll-behavior: contain; }


/* The last two fixed layers, checked against the same rule as the rest.

   .reward is a full-screen grid with a centred card and a fixed pad. On a
   phone with a notch the pad measures from the screen edge, not from the
   usable one, so a tall reward could put its first line under the Island.

   The frame's own two painted bars sit at the bottom of the app behind the tab
   bar; they already resolve --safe-bottom, and are listed here so the next
   person auditing fixed layers finds all of them in one place. */
.reward{
  padding-top: calc(var(--safe-top) + var(--space-6));
  padding-bottom: calc(var(--safe-bottom) + var(--space-6));
  padding-left: max(var(--space-6), var(--safe-left));
  padding-right: max(var(--space-6), var(--safe-right));
}
.reward-card{ max-height: var(--overlay-max-h); overflow-y: auto; overscroll-behavior: contain; }

/* The undo bar clears the tab bar and the home indicator, and now the notch
   sides too: in landscape it is a pill centred on a screen whose left edge is
   under the camera. */
.undobar{
  max-width: min(440px, calc(100vw - var(--safe-left) - var(--safe-right) - var(--space-5) * 2));
}

/* =========================================================================
   Recovery, breaks and estimates.

   Rules, not boxes. The figures on the finish screen are a run of rows under
   a hairline, the way every other region in this system is, and the numbers
   are set in --font-num because every duration in this product is. Nothing
   here draws a card, a fill or a shadow around content.
   ========================================================================= */

/* The three figures at the end of a sitting: estimated, actual, recommended.
   A list, so a screen reader reads label and value as a pair rather than as
   six unrelated strings. */
.recfigs{
  margin: var(--space-5) 0 var(--space-5);
  text-align: left;
  border-top: 1px solid var(--line);
}
.recfig{
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line);
}
.recfig-k{ font-size: var(--fs-2); color: var(--ink-mute); }
.recfig-v{
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-3); font-weight: var(--fw-hi); color: var(--ink);
  white-space: nowrap;
}
/* Why the estimate was out. Quieter than the body copy above it, because it
   is context rather than the thing being said. */
.recnote{ font-size: var(--fs-2); color: var(--ink-mute); margin-bottom: var(--space-4); }

/* What to do with a break. Four kinds of reset, one line each, rotating.
   Rows under rules for the same reason the figures are. */
.brk-ideas{
  width: 100%; max-width: 30ch; margin: var(--space-5) auto 0;
  text-align: left; border-top: 1px solid var(--line);
}
.brk-idea{
  display: flex; align-items: baseline; gap: var(--space-4);
  padding: var(--space-3) 0; border-bottom: 1px solid var(--line);
}
.brk-idea-k{
  flex: 0 0 8.5ch;
  font-size: var(--fs-1); letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--ink-mute);
}
.brk-idea-l{ font-size: var(--fs-2); color: var(--ink-soft); line-height: 1.5; }

/* A break has its own colour (D130): the whole screen takes the rest ground and
   its label the rest hue, so a break is never mistaken for focus time. The
   screen is the plane; nothing inside it gets a fill of its own. */
#brkroot{ background: var(--rest-ground); }
#brkroot .sess-cat{ color: var(--rest); }
#brkroot .brk-idea-k{ color: var(--rest); }

/* A number somebody types: hours of work, hours committed to. It keeps its
   edge, like every other input, because a field without one is not obviously
   a place you can type. Numerals are tabular so the value does not shift
   under the caret. */
.numin{
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--r-control);
  background: var(--ground);
  border: 1px solid var(--line);
  color: var(--ink);
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-3);
  -webkit-appearance: none; appearance: none;
  transition: border-color var(--dur-fast) var(--ease);
}
.numin::-webkit-outer-spin-button, .numin::-webkit-inner-spin-button{
  -webkit-appearance: none; margin: 0;
}
.numin[type="number"]{ -moz-appearance: textfield; }
.numin:hover{ border-color: var(--line-strong); }
.numin:focus-visible{ outline: none; border-color: var(--ink-soft); box-shadow: var(--focus-ring); }
.esthours{ margin-top: var(--space-3); }
.esthours .fnote{ margin-top: var(--space-2); }
/* The add sheet's fixed time: three small fields side by side, each with its
   label above it, wrapping on a narrow phone rather than overflowing. */
.fixtime{ display: flex; flex-wrap: wrap; gap: var(--space-2); }
.fixtime label{ display: flex; flex-direction: column; gap: var(--space-1); flex: 1 1 7rem; min-width: 0; }
.fixtime input{ width: 100%; }
/* The hours field a Custom commitment opens. Sits under the row it belongs
   to rather than inside the date line, which is already two controls wide. */
.tg-custom{ margin-top: var(--space-3); }
.tg-custom .fnote{ margin: 0 0 var(--space-2); }

/* What Sailyn has learned. Rows under rules, each one saying what it is based
   on, because a planner that cannot show its working should not be trusted
   with the plan. */
.learned{ border-top: 1px solid var(--line); }
.learned-row{ padding: var(--space-3) 0; border-bottom: 1px solid var(--line); }
.learned-t{ font-size: var(--fs-3); color: var(--ink); font-weight: var(--fw-med); line-height: 1.45; }
.learned-t .num, .learned-t b{ font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.learned-w{ font-size: var(--fs-2); color: var(--ink-mute); margin-top: var(--space-1); line-height: 1.5; }
/* =========================================================================
   THE SESSION-END SEQUENCE.

   1.6 seconds when a sitting ends. The stage is a stand-in: Task 2.2 built
   the harbour that eventually goes behind it and Task 3.1 put it on Now, and
   moving the window here onto a window there is a task of its own, per
   DECISIONS.md D8. The timing does not move when that happens, which is why
   every duration below is written as the beat it plays rather than as a
   number that looked right.

   Every resting state here is the END state, and the animation is what runs
   the element up from nothing. That is what makes prefers-reduced-motion a
   one-line branch rather than a second stylesheet: switch the animations off
   and the same picture is there, with no motion in it. Nothing in the block
   below moves anything sideways or shakes anything.

   One curve. --ease is the product's curve and there is no second one in this
   file, deliberately.

   Colour is --gold, which is the token for what has been earned, and lamp
   amber is what earned looks like in the harbour. Task 2.1 has since declared
   --lamp under [data-surface="harbour"], and the swap still waits: --lamp is
   measured against --water and is a near-white amber, so on the stand-in
   stage, which has no dark scene behind it, it would be light on light. The
   swap is this stage carrying the attribute once there is a harbour under it.

   Task 2.1 guessed that would be Task 2.2's commit and it was not, and Task
   3.1 did not make it true either. 3.1 put the harbour on Now, but .juice is
   fixed and full-viewport, so this stage still plays centred over the
   instrument ground rather than over the painting. Making the condition true
   means putting the scene behind this window and moving the window onto one
   of its window slots, which is a redesign of this stage rather than a colour
   change, and DECISIONS.md D8 gave it its own task. The condition decides it,
   not the task number: do not swap the token until there is dark water
   behind it.

   There is no gradient anywhere in here: the falloff is a flat wash behind a
   flat core, the same way .reward-glow already does it.
   ========================================================================= */
.juice{
  position:fixed; inset:0; z-index:var(--z-reward);
  display:grid; place-items:center; pointer-events:none;
  padding:var(--space-6);
  opacity:1; transition:opacity var(--dur-fast) var(--ease);
  /* Nothing in here may scroll the page. The bloom already grows past the
     stage by design and the milestone variant below grows past it again, and
     overflow out of a fixed, full-viewport element is still overflow: the page
     gains a sideways scroll for the length of the animation, on an overlay
     that does not even take pointer events. Clip rather than hidden, because
     this must never become a scroll container of its own. */
  overflow:clip;
}
.juice.out{ opacity:0; }

/* The scene dims 12% and comes back up as the light arrives. The window is
   inside it and still dark for the whole dip, so the anticipation costs the
   payoff nothing. */
.juice-scene{
  position:relative; width:220px; height:150px;
  display:grid; place-items:center;
  animation:juiceDim 380ms var(--ease) both;
}
@keyframes juiceDim{
  0%{ filter:brightness(1); }
  32%{ filter:brightness(0.88); }
  100%{ filter:brightness(1); }
}

/* The window. Fills, overshoots to 108% brightness, settles to 100%. */
.juice-window{
  position:relative; width:58px; height:76px;
  border-radius:var(--r-control);
  background:var(--gold);
  opacity:1;
  animation:juiceLight 400ms var(--ease) 120ms both;
}
@keyframes juiceLight{
  0%{ opacity:0; transform:scale(0.94); filter:brightness(0.55); }
  65%{ opacity:1; transform:scale(1); filter:brightness(1.08); }
  100%{ opacity:1; transform:scale(1); filter:brightness(1); }
}

/* The bloom. Expands past the window and is gone by 900ms, which is why its
   resting state is nothing: with no animation there is no bloom to draw. */
.juice-bloom{
  position:absolute; left:50%; top:50%;
  width:190px; height:190px; margin:-95px 0 0 -95px;
  border-radius:50%; background:var(--gold-wash);
  opacity:0;
  animation:juiceBloom 700ms var(--ease) 200ms both;
}
@keyframes juiceBloom{
  0%{ transform:scale(0.3); opacity:0; }
  35%{ opacity:0.95; }
  100%{ transform:scale(1.35); opacity:0; }
}

/* Six to ten motes, each with its own drift, distance, size and delay from
   js/harbour/juice.js. They are never drawn at all under reduced motion:
   a particle that does not drift is a dot. */
.juice-mote{
  position:absolute; left:50%; top:56%;
  width:5px; height:5px; margin:-2.5px 0 0 -2.5px;
  border-radius:50%; background:var(--gold);
  opacity:0;
  animation:juiceMote 700ms var(--ease) calc(200ms + var(--jd, 0ms)) both;
}
@keyframes juiceMote{
  0%{ transform:translate(0,0) scale(var(--js, 1)); opacity:0; }
  22%{ opacity:0.9; }
  100%{ transform:translate(var(--jx, 0), var(--jy, -60px)) scale(calc(var(--js, 1) * 0.55)); opacity:0; }
}

/* The reflection, lagging the light by half a second, which is the whole
   point of it: light arrives, water answers. */
.juice-reflect{
  position:absolute; left:50%; top:62%;
  width:34px; height:56px; margin-left:-17px;
  transform-origin:50% 0;
  background:var(--gold-wash);
  opacity:1;
  animation:juiceReflect 500ms var(--ease) 900ms both;
}
@keyframes juiceReflect{
  0%{ opacity:0; transform:scaleY(0.4); }
  100%{ opacity:1; transform:scaleY(1); }
}

/* The ghost of the next object: one pulse, then it holds. Drawn only when
   there is a harbour for it to stand in, per rule 0.7. Dashed white outline,
   which is what the mockups settled on in 22.1. */
.juice-ghost{
  position:absolute; left:50%; top:50%;
  width:46px; height:46px; margin:-23px 0 0 -23px;
  border:1px dashed var(--gold-wash);
  border-radius:var(--r-control);
  opacity:0.5;
  animation:juiceGhost 200ms var(--ease) 1400ms both;
}
@keyframes juiceGhost{
  0%{ opacity:0; transform:scale(0.94); }
  60%{ opacity:0.75; transform:scale(1.04); }
  100%{ opacity:0.5; transform:scale(1); }
}

/* The counter. The minutes actually sat, never a planned figure, in the
   numeric face because every duration in this product is. It is the reward
   moment's glanceable numeric, which is the second of the two --fs-numeric
   is spent on. */
.juice-count{
  position:absolute; left:0; right:0; top:calc(50% + 108px);
  text-align:center;
  animation:juiceCount 300ms var(--ease) 400ms both;
}
@keyframes juiceCount{
  0%{ opacity:0; transform:translateY(6px); }
  100%{ opacity:1; transform:translateY(0); }
}
.juice-num{
  font-family:var(--font-num); font-size:var(--fs-numeric);
  font-weight:var(--fw-hi); letter-spacing:-0.04em; line-height:1;
  font-variant-numeric:tabular-nums; color:var(--ink-soft);
}

/* Reduced motion. The class is set from matchMedia by juice.js, so the JS
   side stops making motes and writes the counter straight to its final value.
   The query says the same thing in the stylesheet, for the case where it was
   not asked. Both land on the resting states above, which are the end states,
   so what is left is a cross-fade of the same picture. */
.juice.still .juice-scene,
.juice.still .juice-window,
.juice.still .juice-bloom,
.juice.still .juice-reflect,
.juice.still .juice-ghost,
.juice.still .juice-count{ animation:none; }
.juice.still .juice-mote{ display:none; }

@media (prefers-reduced-motion: reduce){
  .juice .juice-scene,
  .juice .juice-window,
  .juice .juice-bloom,
  .juice .juice-reflect,
  .juice .juice-ghost,
  .juice .juice-count{ animation:none; }
  .juice .juice-mote{ display:none; }
}

/* A phone is 375 wide and the stage is the only thing on screen. It does not
   need to be smaller, but the counter under it does need to clear the safe
   area on a screen with a home indicator. */
@media (max-width:420px){
  .juice{ padding:max(var(--space-5), var(--safe-left)) var(--space-5)
          calc(var(--safe-bottom) + var(--space-5)); }
}

/* =========================================================================
   THE MILESTONE VARIANT. Task 1.4.

   Same sequence, at 3x scale: every element above three times the size, every
   beat three times as long. Not a second choreography, so no new keyframe is
   declared here at all, only the duration, the delay and the geometry each
   one already had, tripled. A more specific selector wins on specificity
   alone, so these longhands override the shorthand's without restating the
   keyframe name, the curve or the fill mode.

   The counter's own type does not scale: --fs-numeric is the token for a
   glanceable numeric per tokens.css, and a landmark is still one of the two
   things that token is for, not a reason to invent a bigger number. Only its
   position moves down, to clear the bigger scene above it.

   3x is what the sequence plays at wherever 3x fits, which is every viewport
   from about 720px up. It does not fit on a phone: 660px of stage inside a
   375px screen is not a bigger celebration, it is the middle of one, with the
   counter pushed off the bottom edge. So the three measurements that would
   leave the screen are capped against the viewport and the rest of the stage,
   already well inside a phone at 3x, is left exactly as it is. A phone plays
   the same choreography at about 1.5x, which is still unmistakably not the
   ordinary ending, and every timing is untouched either way.
   ========================================================================= */
.juice.milestone .juice-scene{
  width:min(660px, 92vw); height:min(450px, 56svh);
  animation-duration:1140ms;
}
.juice.milestone .juice-window{
  width:174px; height:228px;
  animation-duration:1200ms; animation-delay:360ms;
}
/* Carried on a property so the offset cannot drift from the size: the bloom is
   centred by pulling itself back half its own width, and two capped lengths
   that have to agree are one length used twice. */
.juice.milestone .juice-bloom{
  --jb:min(570px, 78vw);
  width:var(--jb); height:var(--jb);
  margin:calc(var(--jb) / -2) 0 0 calc(var(--jb) / -2);
  animation-duration:2100ms; animation-delay:600ms;
}
.juice.milestone .juice-mote{
  animation-duration:2100ms; animation-delay:calc(600ms + var(--jd, 0ms));
}
.juice.milestone .juice-reflect{
  width:102px; height:168px; margin-left:-51px;
  animation-duration:1500ms; animation-delay:2700ms;
}
.juice.milestone .juice-ghost{
  width:138px; height:138px; margin:-69px 0 0 -69px;
  animation-duration:600ms; animation-delay:4200ms;
}
/* The counter clears the bigger stage, and on a short screen it clears it by
   as much as there is rather than by 324px: this is positioned against .juice,
   which is the viewport, so an uncapped 324px puts the one number the sequence
   exists to show below the bottom edge of a phone. */
.juice.milestone .juice-count{
  top:calc(50% + min(324px, 32svh));
  animation-duration:900ms; animation-delay:1200ms;
}
/* =========================================================================
   ONE CURVE.

   --ease, defined once in tokens.css, is the product's only easing curve for
   a transition or an arrival animation. Nothing in css/ names a second one:
   every ad-hoc "ease", "linear" and one-off duration that used to sit beside
   a component's own rules has been replaced in place with --ease and the
   three-value duration scale (--dur-fast/base/slow, DESIGN.md's Motion
   section). Two declarations are the deliberate exception and stay put: the
   hold-to-stop fill (css/day.css, .hold-fill) and the session ring
   (css/takeover.css, .ring-fg) both track real elapsed time linearly rather
   than arriving at a value, which is a different kind of motion than this
   file governs.

   This file holds only what is genuinely shared: a keyframe more than one
   component references. Everything component-specific stays where it was
   written, next to the rule it belongs to, per docs/css-split.md.
   ========================================================================= */
@keyframes rise{from{ opacity:0; transform:translateY(12px); }to{ opacity:1; transform:translateY(0); } }

/* The one breathing-dot pulse: syncing, offline, and the mic listening state
   all used their own copy of this same shape under three different names,
   one of them (day.css's micPulse) with empty keyframe bodies that animated
   nothing. One definition now; each caller picks its own duration. */
@keyframes pulse{0%, 100%{ opacity:1; }50%{ opacity:.35; } }
/* =========================================================================
   THE HARBOUR.

   Almost nothing, and that is the shape of it. The scene is SVG built in
   js/harbour/scene.js, so every mass, every window and every ghost is drawn
   there against one coordinate space. What CSS owns is what tokens.css says
   it owns: the field the plate sits in, and the block's own edges.

   The field is --water rather than nothing. Once the plate is a painted
   image, the moment before it decodes is the only moment this rule is
   visible, and a scene that arrives out of the sea it is a picture of does
   not flash the page white on the way in.

   The one rule that reads a token carries data-surface="harbour" in its own
   selector rather than relying on the wrapper having it. That is
   test/surfacecheck.js's rule, written in Task 2.1 and aimed at exactly this
   commit: a scoped token may only be read from a selector that names the
   scope, because @theme and :root both resolve an unscoped read to nothing
   and a rule that silently paints nothing is the worst way for this to fail.
   So the declaration and the palette arrive together, by construction, and
   no fallback value is written here. A second copy of a token's value is the
   rot the token exists to prevent.

   There is no dark-theme block and there will not be one, per tokens.css.
   The harbour is the one dark thing in a light product and is the same
   painting in both themes.

   Nothing here animates. The lighting sequence at the end of a session is
   css/juice.css and Task 1.1; the sky changing is four hours apart and is a
   different picture rather than a transition between two. So there is no
   prefers-reduced-motion branch, because there is no motion to reduce.
   ========================================================================= */
[data-surface="harbour"].harbour{
  position:relative;
  background:var(--water);
  /* The bloom in juice.css already has to clip; this clips for a duller
     reason. preserveAspectRatio="xMidYMid slice" scales the crop to cover,
     so on any viewport the crop does not fit exactly there is painting
     outside the box by design, and clip keeps it from being scrollable
     without making this a scroll container of its own. */
  overflow:clip;
}
/* An SVG is inline by default, which leaves a line box under it and a few
   pixels of ground between the painting and the hard edge below it. */
.harbour-svg{ display:block; width:100%; height:100%; }

/* The two crops. Each carries its own aspect so that a harbour with no
   height set by its consumer still has the shape of the crop it is, which is
   what makes harbourScene() worth calling by hand while the flag is off.
   Task 3.1 gives the hero a real height, 45% of the screen, and the aspect
   stops applying the moment it does.

   Square, both of them. UI-SPEC section 7 measures about 7px on the
   thumbnail and section 12 lists that as conflict 1 with tokens.css, which
   caps content radius at 3px. Task 2.2 drew both crops square and left the
   conflict open rather than resolving it inside a task about a renderer;
   decision D12 answered it in Task 3.4, in tokens.css's favour, so this is
   the decision now and not a holding position. css/progress-board.css pins
   the thumbnail to --r-card where it mounts it, which is where somebody
   would come to round it.

   No border, no shadow, no fade and no mask on the hero, and that absence is
   the specification rather than an omission. UI-SPEC section 1: "the harbour
   ends at a straight horizontal line with no gradient, no fade and no
   rounding; the next pixel is the instrument ground. Do not blend them." */
.harbour--hero{ aspect-ratio:393 / 384; }
.harbour--thumb{ aspect-ratio:393 / 155; }

/* Arranging the harbour, Task 4.2. The picture in the sheet scrolls the sheet
   vertically and is dragged horizontally, so only horizontal pans are the
   page's; the rows under it are the ordinary one-line rule-separated row. */
.arrange-scene{ margin-bottom:var(--space-3); touch-action:pan-y; }
.arrange-scene [data-move]{ cursor:grab; }
.arrange-list{ display:flex; flex-direction:column; margin-top:var(--space-3); }
.arrange-row{ display:flex; align-items:center; justify-content:space-between;
  gap:var(--space-3); padding:var(--space-3) 0; border-top:1px solid var(--line); }
.arrange-nm{ font-size:var(--fs-3); color:var(--ink); min-width:0; }
/* =========================================================================
   PLAN, ON THE CUE CHIPS. Task 3.2.

   Last in the cascade because every rule here is scoped to one screen and
   owns nothing else. Nothing above needs beating, so nothing here belongs in
   the world layer.

   The gutter is not repeated in this file. #app main already pads every
   screen by --space-5 on both sides, so a rule that set its own horizontal
   padding would draw a second gutter inside the first. The row hairline is an
   inset box-shadow on a full-width row for the same reason: it then spans the
   content width exactly, which is what makes a column of durations scan.

   UI-SPEC section 9 states this screen's absences and they are why most of
   these rules are one line: no shadow on content, no card, no filled panel
   behind any region, no radius on content, no icon carrying colour, no
   gradient.

   The accent appears once, on the plus, which is a filled button like every
   other primary in the product. UI-SPEC also puts the live chip in it, and
   that half is section 12 conflict 2 against tokens.css rule 2 and is still
   open, so the chip is said in ink and weight instead. Conflict 3, one band
   per screen, therefore does not arise: there is one.
   ========================================================================= */

/* ---- The title and its date, on one baseline --------------------------- */
.phead{
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}
.phead-d{
  font-family: var(--font-num);
  font-size: var(--fs-3);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---- Timetable and semester, one tap from Plan (D42) --------------------
   Two quiet actions under the title, an underline rather than a box, the way
   the other secondary actions on these screens are drawn. */
.ptools{ display: flex; flex-wrap: wrap; gap: var(--space-4); margin: var(--space-2) 0 var(--space-4); }
.ptools .btn--tertiary{
  width: auto; min-height: 32px; padding-left: 0; padding-right: 0;
  border-color: transparent; border-bottom-color: var(--line); border-radius: 0;
  font-size: var(--fs-2);
}

/* ---- The chip row ------------------------------------------------------
   Separate pills, not a segmented control. UI-SPEC section 7 is explicit
   that Progress's pill-in-pill and this row look similar and are not the
   same component, so they share no rule.

   It scrolls rather than wrapping, because six chips do not fit in the
   content width and a second line of chips reads as a second row of
   something. No scrollbar: the chip cut off at the edge is the affordance. */
.pcues{
  display: flex;
  gap: 9px;
  margin: var(--space-3) 0;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.pcues::-webkit-scrollbar{ display: none; }
.pcue{
  flex: 0 0 auto;
  height: 33px;
  padding: 0 var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--ground);
  color: var(--ink-mute);
  font-size: var(--fs-2);
  font-weight: var(--fw-body);
  line-height: 1;
  cursor: pointer;
}
/* The live chip is said in ink and weight, not in the accent.
 *
 * UI-SPEC section 6 puts the active chip's border and label in --accent. That
 * is conflict 2 in section 12, against tokens.css rule 2, which permits the
 * accent on an edge only for focus and the live band, and it is still open.
 * Task 2.3 hit the same conflict on the Progress segmented control and took
 * the geometry without the colour; this is the same answer, so the two
 * controls do not resolve one open question two different ways.
 *
 * A filled chip is not the alternative: that is a tint, which the same rule
 * refuses and the mockup does not show either. */
.pcue.is-on{
  border-color: var(--line-strong);
  color: var(--ink);
  font-weight: var(--fw-med);
}
.pcue:focus-visible{ box-shadow: var(--focus-ring); outline: none; }

/* ---- The commitment, said back in the form it was made in -------------- */
.pcommit{
  margin: 0 0 var(--space-4);
  font-size: var(--fs-3);
  color: var(--ink-soft);
}

/* ---- A group: a header, then its rows, then air ------------------------
   No container, no indent, no fill. A group is a run of rows under a label,
   which is "rules, not boxes" applied to a list rather than to a panel. */
.pgroup{ margin: 0 0 37px; }
.pgroup:last-of-type{ margin-bottom: 0; }
.pgroup-h{
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: 0 0 var(--space-2);
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
}
.pgroup-nm{
  flex: 1 1 auto;
  font-size: var(--fs-1);
  font-weight: var(--fw-med);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
}
.pgroup-n{
  font-family: var(--font-num);
  font-size: var(--fs-1);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}
.pgroup-c{
  width: 11px; height: 11px;
  flex: 0 0 auto;
  color: var(--ink-mute);
  transition: transform var(--dur-fast) var(--ease-soft);
}
.pgroup.is-shut .pgroup-c{ transform: rotate(-90deg); }

/* ---- One row, one line -------------------------------------------------
   UI-SPEC section 8's one-line variant. The rule runs under every row
   including the last of a group. */
.prow{
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 49px;
  box-shadow: inset 0 -1px 0 var(--line);
}
.prow-nm{
  flex: 1 1 auto;
  font-size: var(--fs-3);
  font-weight: var(--fw-body);
  color: var(--ink);
  min-width: 0;
}
.prow-min{
  flex: 0 0 auto;
  font-family: var(--font-num);
  font-size: var(--fs-3);
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

/* The box. Open is a ring with nothing in it; done is filled with a check.
   Both are 21px and both sit on the gutter, so ticking one moves nothing. */
.pbox{
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 21px; height: 21px;
  padding: 0;
  border: 1px solid var(--ink-soft);
  border-radius: var(--r-pill);
  background: none;
  color: transparent;
  cursor: pointer;
}
.pbox svg{ width: 12px; height: 12px; }
.pbox.is-done{
  border-color: var(--ink-mute);
  background: var(--ink-mute);
  color: var(--ground);
  cursor: default;
}

/* A completed row is greyed and stays exactly where it was.
   No strikethrough, no size change, no weight change, no reordering and no
   disappearing. Addendum 22.1 and UI-SPEC section 8 call this one not
   negotiable: finished work is the most rewarding thing on the screen and
   styling it as deletion says the opposite. */
.prow.is-done .prow-nm,
.prow.is-done .prow-min{ color: var(--ink-mute); text-decoration: none; }

/* ---- The plus ---------------------------------------------------------
   The only creation entry point in the app, decision D-A. No shadow: UI-SPEC
   section 9 gives the FAB none either, so it sits on the ground rather than
   over it. Clear of the tab bar and of the home indicator under it. */
.pfab{
  position: fixed;
  right: var(--space-5);
  bottom: calc(var(--nav-h) + max(var(--space-2), var(--safe-bottom)) + 19px);
  display: grid;
  place-items: center;
  width: 45px; height: 45px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-ink);
  cursor: pointer;
  z-index: 40;
}
.pfab svg{ width: 16px; height: 16px; }

/* The four options behind it. Rows, like everything else on this screen. */
.plusopts{ display: flex; flex-direction: column; }
.timp-list{ margin:var(--space-3) 0 0; padding-left:var(--space-5); font-size:var(--fs-2); color:var(--ink-soft); }
.timp-list .num{ color:var(--ink-mute); margin-left:var(--space-2); }
.plusopt{
  display: block;
  width: 100%;
  padding: var(--space-3) 0;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
  box-shadow: inset 0 -1px 0 var(--line);
}
.plusopt b{
  display: block;
  font-size: var(--fs-3);
  font-weight: var(--fw-med);
  color: var(--ink);
}
.plusopt em{
  display: block;
  margin-top: 2px;
  font-size: var(--fs-2);
  font-style: normal;
  color: var(--ink-mute);
}

/* ---- The reading list, D44 ---------------------------------------------
   A book is a row like any other here: a rule under it, its name and how far
   into it you are, Read now or where it stands, and removing it at the end.
   The page counts are numbers, so they are set in the numeric face. */
.books{ display: flex; flex-direction: column; margin-bottom: var(--space-5); }
.book{ display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3) 0;
       box-shadow: inset 0 -1px 0 var(--line); }
.book-body{ flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.book-nm{ font-size: var(--fs-3); font-weight: var(--fw-med); color: var(--ink); overflow-wrap: anywhere; }
.book-meta{ font-family: var(--font-num); font-size: var(--fs-2); color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.book-planned{ font-size: var(--fs-2); color: var(--ink-soft); }
.book-read{ display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3); margin-top: var(--space-2); }
.book-by{ display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--fs-2); color: var(--ink-mute); }
.book-read .btn--secondary{ width: auto; }
/* The reading pace, one line that says what sittings are sized by and opens
   onto the number for anybody who wants to set it. */
.book-pace{ margin-top: var(--space-2); }
.book-pace summary{ font-size: var(--fs-2); color: var(--ink-soft); cursor: pointer; }
.book-pace .book-by{ margin-top: var(--space-2); }
.book-pace .fnote{ display: block; margin-top: var(--space-1); }
/* Revision, on a book and on a goal: one question and, once there is a day,
   the day. The same component in both places, because it is one setting. */
.book-kind{ font-size: var(--fs-1); color: var(--ink-mute); letter-spacing: var(--track-label); text-transform: uppercase; }
.book-rev, .goal-rev{ margin-top: var(--space-2); }
.rev-field{ display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--fs-2); color: var(--ink-soft); }
.rev-field select{ width: auto; }
.rev-when{ display: block; margin-top: var(--space-1); font-size: var(--fs-2); color: var(--ink-mute); }

/* From the rail width the tab bar is gone, so the offset it was clearing is
   gone with it and the plus sits on the bottom gutter instead. */
@media (min-width: 880px){
  .pfab{ bottom: var(--space-5); }
}
/* Put down, not gone. G1: a paused goal or book stays in the list and reads as
   quieter than the rest, and the word Paused is beside it, because opacity on
   its own is not a state anybody can be sure of. */
.goal--paused, .book--paused{ opacity: .62; }
.goal--paused .goal-head, .book--paused .book-body{ color: var(--ink-soft); }
.goal-paused-l{ letter-spacing: var(--track-label); text-transform: uppercase;
  font-size: var(--fs-1); color: var(--ink-mute); }
/* The two controls sit on the head row beside the remove control, so they are
   held to its size rather than to a full-width button's. */
.goal-pause, .goal-resume{ flex: 0 0 auto; align-self: start; padding: 4px var(--space-3); }
/* How far through a book. G2: the same bar the goals use, so a reader has one
   thing to learn, and the numbers sit beside it because the bar is the shape
   and the figures are the fact. */
.book-bar{ margin-top: var(--space-2); }

/* Search on Plan (§6): one box at the top of Your timetable, the answers in place of the grid, and the
   rest of the screen stepping aside while there is something typed. */
.psearch{ position:relative; margin:0 0 var(--space-3); }
.psearch-in{ width:100%; padding-inline-end:var(--space-6); }
.psearch-x{ position:absolute; inset-inline-end:var(--space-1); top:50%; transform:translateY(-50%);
  min-width:32px; min-height:32px; border:0; background:none; color:var(--ink-mute); font-size:var(--fs-4); cursor:pointer; }
.psearch-x[hidden]{ display:none; }
#main[data-searching] .ptools, #main[data-searching] .pcues, #main[data-searching] .planbody,
#main[data-searching] .fortbody, #main[data-searching] .page-side{ display:none; }
.psr-count, .psr-none{ margin:0 0 var(--space-2); font-size:var(--fs-2); color:var(--ink-mute); }
.psr-list{ list-style:none; margin:0 0 var(--space-5); padding:0; }
.psr-list li + li{ border-top:1px solid var(--line); }
.psr{ display:grid; grid-template-columns:auto 1fr auto; gap:var(--space-3); align-items:baseline; width:100%;
  min-height:48px; padding:var(--space-2) 0; border:0; background:none; color:var(--ink); text-align:start; cursor:pointer; font:inherit; }
.psr-when{ display:flex; flex-direction:column; min-width:9ch; font-size:var(--fs-2); color:var(--ink-mute); }
.psr-name{ display:flex; flex-direction:column; min-width:0; overflow-wrap:anywhere; }
.psr-place{ font-size:var(--fs-2); color:var(--ink-mute); }
.psr-tag{ font-size:var(--fs-1); color:var(--ink-mute); }
.psr.is-done .psr-name{ color:var(--ink-mute); }

/* Wellbeing's meals, medication and cycle (§3, §4), in the Settings pane and
   their sheets: rows under a rule, the calendar a grid of days. */
.wbmeal{ display:flex; flex-direction:column; gap:var(--space-2); padding:var(--space-2) 0; border-top:1px solid var(--line); }
.medlist, .medhist{ list-style:none; margin:0 0 var(--space-2); padding:0; }
.medlist li + li, .medhist li + li{ border-top:1px solid var(--line); }
.medrow{ display:flex; flex-direction:column; align-items:flex-start; width:100%; min-height:48px; padding:var(--space-2) 0;
  border:0; background:none; color:var(--ink); text-align:start; font:inherit; cursor:pointer; }
.medname{ font-weight:var(--fw-med); }
.medmeta{ font-size:var(--fs-2); color:var(--ink-mute); }
.medhist li{ display:grid; grid-template-columns:auto 1fr auto; gap:var(--space-3); padding:var(--space-1) 0; font-size:var(--fs-2); }
.medtime{ margin:0 var(--space-2) var(--space-2) 0; }
.cyc-nav{ display:flex; align-items:center; justify-content:space-between; margin:0 0 var(--space-2); }
.cyc-grid{ display:grid; grid-template-columns:repeat(7, minmax(0, 1fr)); gap:2px; margin:0 0 var(--space-3); }
.cyc-w{ text-align:center; font-size:var(--fs-1); color:var(--ink-mute); }
.cyc-d{ position:relative; min-height:40px; border:1px solid var(--line); border-radius:var(--r-control); background:var(--ground); color:var(--ink); font:inherit; cursor:pointer; }
.cyc-d.is-pad{ border:0; background:none; cursor:default; }
.cyc-d.is-today{ outline:2px solid var(--ink-mute); outline-offset:-2px; }
.cyc-d.is-logged, .cyc-sw.is-logged{ background:var(--rest-ground); }
.cyc-d.is-expected, .cyc-sw.is-expected{ background:var(--rest-wash); border-style:dashed; }
.cyc-dot{ position:absolute; bottom:4px; left:50%; width:4px; height:4px; margin-left:-2px; border-radius:var(--r-pill); background:var(--ink-mute); }
.cyc-key{ display:flex; align-items:center; gap:var(--space-2); font-size:var(--fs-2); color:var(--ink-mute); }
.cyc-sw{ display:inline-block; width:12px; height:12px; border:1px solid var(--line); border-radius:var(--r-control); }
/* The weekly planning routine (18 October, plan/weekly.js). */
.wk-h{ font-size:var(--fs-4); font-weight:var(--fw-hi); margin:var(--space-1) 0 var(--space-3); }
.wk-step{ min-height:8rem; }
.wk-big{ font-family:var(--font-num); font-size:var(--fs-6); margin:0; }
.wk-done{ margin:var(--space-2) 0 0; padding-left:var(--space-5); }
.wk-load{ list-style:none; margin:0; padding:0; }
.wk-day{ display:grid; grid-template-columns:5.5rem 1fr auto; gap:var(--space-2); align-items:center; padding:var(--space-2) 0; border-bottom:1px solid var(--line); }
.wk-day.is-full .wk-dl{ color:var(--danger); }
.wk-day.is-off{ color:var(--ink-mute); }
.wk-dn{ font-weight:var(--fw-med); }
.wk-dl{ font-size:var(--fs-2); color:var(--ink-soft); }
/* Too much right now (plan/recovery.js): one line and a way out. */
.calmline{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2) var(--space-3); margin:var(--space-4) 0; padding:var(--space-3) 0; border-top:1px solid var(--line); border-bottom:1px solid var(--line); }
.calmline p{ margin:0; flex:1 1 14rem; color:var(--ink-soft); }
.nowcalm{ margin-top:var(--space-2); }
/* The command bar (18 October, ui/cmdk.js). */
.cmdksheet{ padding-top:var(--space-4); }
.cmdk-in{ width:100%; font-size:var(--fs-4); }
.cmdk-list{ list-style:none; margin:var(--space-3) 0 0; padding:0; }
.cmdk-it{ display:block; width:100%; text-align:left; padding:var(--space-2) var(--space-3); border:0; background:none; border-radius:var(--r-control); cursor:pointer; }
.cmdk-it.is-on{ background:var(--ground-deep); box-shadow:inset var(--band-w) 0 0 var(--ink); }
.cmdk-hint{ margin-top:var(--space-3); }
/* The private wellbeing log (18 October, plan/wbmore.js). */
.wblog{ width:100%; border-collapse:collapse; font-size:var(--fs-2); margin-top:var(--space-2); }
.wblog th, .wblog td{ text-align:left; padding:var(--space-1) var(--space-2) var(--space-1) 0; border-bottom:1px solid var(--line-soft); }
.wblog th{ font-weight:var(--fw-med); color:var(--ink-mute); }
/* =========================================================================
   TASKS, AS THE BACKLOG. Task 3.3.

   Four rules, and that is the whole screen. UI-SPEC section 11 gives Tasks
   the Plan grammar and no mockup of its own, so the title, the groups, the
   counts, the chevrons and the row all come from css/plan-cues.css unchanged.
   Sharing them is the point: section 8 says the row is one component across
   Now, Plan and Tasks, and two stylesheets drawing it is how it stops being
   one.

   So what is here is only what Tasks needs and Plan does not: the row's text
   is a button, because the four controls the old row carried have to live
   somewhere and section 8 says not in the row.
   ========================================================================= */

/* The row's text, as the door to everything behind it.
 *
 * It has to leave the row's geometry exactly as it was, so it is a transparent
 * button that lays its two children out the way .prow laid them out itself:
 * the name takes the slack, the duration sits against the content gutter. A
 * wrapper that introduced its own padding or font would move a column of
 * durations that Plan and Tasks are supposed to share. */
.prow-open{
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  gap: var(--space-4);
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.prow-open:focus-visible{ box-shadow: var(--focus-ring); outline: none; }

/* The sheet's own head: the priority pips it inherited from the row, and the
   line of facts the row can no longer carry. No rule, no fill: it sits above
   the actions and the actions already carry their own hairlines. */
.tact-head{
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0 0 var(--space-3);
}
.tact-meta{
  margin: 0;
  font-size: var(--fs-2);
  color: var(--ink-mute);
}

/* The shopping list inside a task sheet. D102.
 *
 * A run of rows under a rule with a heading, which is what a region is here:
 * no card, no fill under the content, one hairline on the top edge. A ticked
 * item greys and keeps its words, because a strikethrough is what this product
 * does not do to finished work.
 *
 * The box is drawn rather than an <input>, for the same reason the completed
 * rows on Plan use a span: this is the row's own affordance and the tick is
 * information rather than a form field. */
.tlist{ border-top: 1px solid var(--line); margin-top: var(--space-4); padding-top: var(--space-3); }
.tlist-h{ font-size: var(--fs-1); letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--ink-mute); font-weight: var(--fw-hi); margin-bottom: var(--space-2);
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.tlist-n{ font-family: var(--font-num); font-variant-numeric: tabular-nums;
  letter-spacing: 0; text-transform: none; }
.tlist-row{ display: flex; align-items: center; gap: var(--space-3); width: 100%;
  background: none; border: none; padding: var(--space-2) 0; text-align: left;
  font: inherit; color: var(--ink); cursor: pointer; }
.tlist-row + .tlist-row{ border-top: 1px solid var(--line); }
.tlist-row:focus-visible{ outline: none; box-shadow: var(--focus-ring); border-radius: var(--r-control); }
.tlist-box{ flex: 0 0 auto; width: 16px; height: 16px; border: 1.5px solid var(--line-strong);
  border-radius: var(--r-control); position: relative; }
.tlist-row.is-done .tlist-box{ background: var(--ink-mute); border-color: var(--ink-mute); }
.tlist-row.is-done .tlist-box::after{ content: ""; position: absolute; left: 4px; top: 1px;
  width: 5px; height: 9px; border-right: 2px solid var(--ground); border-bottom: 2px solid var(--ground);
  transform: rotate(45deg); }
.tlist-nm{ font-size: var(--fs-3); line-height: 1.4; }
.tlist-row.is-done .tlist-nm{ color: var(--ink-mute); }

/* The arrow on a row that carries a list, and the list under it. D103.
 *
 * On Now, Plan and Tasks alike, because the rows on all three are one row. The
 * arrow carries the count, so a folded list still says how much is left without
 * being opened, and it points down when open and right when folded. The list
 * under the row is indented to the row's title, not to its box, so the items
 * read as belonging to it. No card and no fill: one hairline above each item,
 * which is how a region separates its parts everywhere else here. */
.lrow-arrow{ flex: 0 0 auto; display: inline-flex; align-items: center; gap: var(--space-1);
  background: none; border: none; padding: var(--space-1) var(--space-2); margin-left: auto;
  color: var(--ink-mute); font: inherit; font-size: var(--fs-2); cursor: pointer;
  border-radius: var(--r-control); }
.lrow-arrow:hover{ color: var(--ink); }
.lrow-arrow:focus-visible{ outline: none; box-shadow: var(--focus-ring); }
.lrow-arrow svg{ transform: rotate(-90deg); transition: transform var(--dur-fast) var(--ease); }
.lrow-arrow.is-open svg{ transform: none; }
.lrow-n{ font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.lrow-list{ padding: 0 0 var(--space-2) calc(16px + var(--space-3)); }
@media (prefers-reduced-motion: reduce){ .lrow-arrow svg{ transition: none; } }
:root[data-motion="still"] .lrow-arrow svg{ transition: none; }

/* D104: an item is its tick and a cross beside it, and one line under the items
 * says how more gets on. The rule sits between items rather than between the
 * two buttons of one item. */
.tlist-item{ display: flex; align-items: center; gap: var(--space-2); }
.tlist-item + .tlist-item{ border-top: 1px solid var(--line); }
.tlist-item .tlist-row{ flex: 1 1 auto; min-width: 0; }
.tlist-x{ flex: 0 0 auto; display: inline-grid; place-items: center; width: 32px; height: 32px;
  background: none; border: none; padding: 0; color: var(--ink-mute); cursor: pointer;
  border-radius: var(--r-control); }
.tlist-x:hover{ color: var(--ink); }
.tlist-x:focus-visible{ outline: none; box-shadow: var(--focus-ring); }
.tlist-more{ display: block; background: none; border: none; padding: var(--space-2) 0 0;
  font: inherit; font-size: var(--fs-2); color: var(--ink-mute); cursor: pointer; text-align: left; }
.tlist-more:hover{ color: var(--ink); }
.tlist-more:focus-visible{ outline: none; box-shadow: var(--focus-ring); border-radius: var(--r-control); }
/* On the session screen, under the time, held to the session's own measure. */
.sesslist{ width: 100%; max-width: 360px; margin: var(--space-4) auto 0; text-align: left; }
/* Shopping trips in Insights: the errand and its day, then what was on it. */
.trip + .trip{ border-top: 1px solid var(--line); }
.trip{ padding: var(--space-2) 0; }
.trip-h{ display: flex; justify-content: space-between; gap: var(--space-3); font-size: var(--fs-3); }
.trip-nm{ font-weight: var(--fw-med); }
.trip-d{ color: var(--ink-mute); font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.trip-items{ font-size: var(--fs-2); color: var(--ink-soft); line-height: 1.5; }
.trip-left{ font-size: var(--fs-2); color: var(--ink-mute); line-height: 1.5; }
/* Steps inside a task (18 October): the list's own grammar, an input to add. */
.steps{ margin-top: var(--space-3); }
.steps-off{ margin-top: var(--space-2); }
.nowpick-steps{ margin-left: var(--space-2); }
.steps-new{ margin-top: var(--space-2); flex-wrap: nowrap; }
.steps-new input{ flex: 1 1 auto; min-width: 0; }
.steps-offbtn{ margin-top: var(--space-2); }
.nowpick-step{ margin: var(--space-2) 0 0; font-size: var(--fs-2); color: var(--ink-soft); }
/* =========================================================================
   PROGRESS, COMPOSED. Task 3.4, decision D12.

   docs/UI-SPEC.md section 7. Three blocks lead Story: the stat row, the week
   as bars, and the harbour cropped wide.

   Every declaration that reads a data hue names [data-surface="progress"] in
   its own selector. That is test/surfacecheck.js's rule and it exists because
   @theme and :root both resolve a scoped token to nothing, so a rule written
   without the scope paints nothing and says nothing about it. The scope is on
   the three wrappers rather than on the screen, because data-surface names
   the material a thing is painted in rather than where it sits.

   There is no accent anywhere in this file. D12 closed section 12's conflict
   2 in tokens.css's favour, so the segmented control above these blocks stays
   ink and weight, and the three data hues are D1's exception and are on data
   only. Nothing here is a fill under content either: the stat row separates
   with a hairline, the week's gridlines are hairlines, and the one filled
   shape on the screen is a bar, which is a quantity rather than a container.
   ========================================================================= */

/* ---- the three stats ---------------------------------------------------
   Three equal columns with a hairline between them, running the full height
   of the row, which is UI-SPEC's own drawing: the rule is a separator and not
   a border, so it is on one edge of two of the three and nowhere else. */
.pgstats{
  /* As many equal columns as there are stats: three with the weekly rhythm
     on, two without it, and never an empty third (Task 4.5). */
  display:grid; grid-auto-flow:column; grid-auto-columns:1fr;
  gap:0; margin:0;
}
.pgstat{
  display:flex; flex-direction:column; align-items:flex-start;
  gap:var(--space-2); padding:0 var(--space-2);
  min-width:0;
}
.pgstat:first-child{ padding-left:0; }
.pgstat:last-child{ padding-right:0; }
.pgstat + .pgstat{ border-left:1px solid var(--line); }

.pgstat-l{
  font-size:var(--fs-1); color:var(--ink-mute); line-height:1.3;
}
/* D12 on section 12's conflict 7: --fs-6, not the mockup's 36px and not
   --fs-numeric, which is 64px and belongs to the session timer and the
   reward. A number is set in --font-num and tabular, per tokens.css.

   Clamped rather than fixed, and that is arithmetic rather than caution. A
   third of the content width on a 393px phone is about 111px, and "12.4h" at
   40px is about 106px before the column's own padding, so --fs-6 is exactly
   at the point where three columns stop fitting on the screen the mockup was
   drawn at. The floor is --fs-5 and the ceiling is --fs-6, both real steps of
   the scale, so this adds no size to it; the middle term is what picks
   between them. css/screens/now.css clamps the hero's height the same way.

   The value wraps rather than overflowing, which is what a locale with a long
   unit needs: "12,4 Std." is twice the width of "12.4h" at any size, and a
   number half off the edge of its column is worse than one on two lines. */
.pgstat-n{
  font-size:clamp(var(--fs-5), 8.8vw, var(--fs-6));
  line-height:1.05; color:var(--ink);
  font-weight:var(--fw-med); letter-spacing:var(--track-title);
}
.pgstat-d{
  font-size:var(--fs-1); color:var(--ink-mute); line-height:1.3;
  min-height:1.3em;
}
/* Green only on a real rise. --good is already measured at 4.88:1 and is the
   same green Progress's delta line has always used, so there is no fourth
   green and this is not one of the three data hues. */
.pgstat-d--up{ color:var(--good); }

/* Scoped to the stat row, with every property the reward's particle sets
   set back. takeover.css has a .spark of its own, the floating particle,
   absolutely placed, 7px, round and filled, and unscoped it landed on these
   three and piled them in the middle of the row as one grey blob. */
.pgstat .spark{ position:static; display:block; width:100%; max-width:87px; height:auto;
  margin-top:var(--space-1); border-radius:0; background:none; }
.spark-line{ fill:none; }
.spark-wash{ stroke:none; }

[data-surface="progress"] .spark[data-hue="1"] .spark-line{ stroke:var(--data-1); }
[data-surface="progress"] .spark[data-hue="1"] .spark-wash{ fill:var(--data-1-wash); }
[data-surface="progress"] .spark[data-hue="2"] .spark-line{ stroke:var(--data-2); }
[data-surface="progress"] .spark[data-hue="2"] .spark-wash{ fill:var(--data-2-wash); }
[data-surface="progress"] .spark[data-hue="3"] .spark-line{ stroke:var(--data-3); }
[data-surface="progress"] .spark[data-hue="3"] .spark-wash{ fill:var(--data-3-wash); }

/* ---- the week ----------------------------------------------------------
   The heading, the total on its baseline and the hairline above them are
   region()'s, so this file draws only the plot. It is one box: the gridlines
   are positioned from the bottom of it and so are the bars, so both read the
   same number and neither can drift from the other.

   34px of gutter on the left for the axis labels, 22px under for the day
   names, both stated once as the plot's own padding. */
.pgweek-plot{ position:relative; }
.pgweek-grid{ position:absolute; inset:0 0 22px 34px; }
/* Fainter than a row hairline, per UI-SPEC. --line-soft is the token that
   already means that and is what .bar's own track is drawn in. */
.pgweek-g{
  position:absolute; left:0; right:0; height:0;
  border-top:1px solid var(--line-soft);
}
/* Sitting on the line rather than above it: the label and the gridline are
   one reading, and half its own height is what centres it without knowing
   what that height is. */
.pgweek-y{
  position:absolute; right:100%; bottom:0; transform:translateY(50%);
  padding-right:var(--space-2); white-space:nowrap;
  font-size:var(--fs-1); color:var(--ink-mute);
}
.pgweek-bars{
  position:relative; display:grid; grid-template-columns:repeat(7, 1fr);
  align-items:end; gap:var(--space-2);
  height:130px; padding-left:34px; padding-bottom:22px;
}
.pgweek-c{ position:relative; height:100%; display:flex; flex-direction:column; justify-content:flex-end; }
/* Square tops and one hue for all seven: this is one series, and seven
   colours would say it was seven. */
.pgweek-b{
  width:20px; max-width:100%; margin:0 auto; border-radius:0;
  min-height:0;
}
[data-surface="progress"] .pgweek-b{ background:var(--data-3); }
.pgweek-d{
  position:absolute; left:0; right:0; bottom:-20px; text-align:center;
  font-size:var(--fs-1); color:var(--ink-mute);
}

/* ---- your harbour ------------------------------------------------------
   The whole block is the way into the harbour on Now, so the whole block is
   the control. No fill, no shadow and no border: a run of content under a
   heading, the same as every other region, with the painting beneath it. */
.pgharb{
  display:block; width:100%; text-align:left;
  background:none; border:0; padding:0; margin:0;
  color:inherit; font:inherit; cursor:pointer;
}
.pgharb-head{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--space-3); padding-bottom:var(--space-3);
}
.pgharb-txt{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.pgharb-s{ font-size:var(--fs-3); color:var(--ink-mute); }
.pgharb-go{ flex:none; color:var(--ink-mute); }
.pgharb:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; }

/* The thumbnail is square, which is D12 on section 12's conflict 1: the
   mockup measures about 7px, tokens.css caps content radius at 3px, and the
   hero on Now is already a hard edge. Two answers to where the painting stops
   would be one too many. css/harbour.css draws both crops and says the same
   thing where it draws them. */
.pgharb .harbour{ border-radius:var(--r-card); }

/* ---- the setup under it -------------------------------------------------
   Decision D33: the room, the light, the home and the coins are one setup
   under the harbour they belong to. The room takes the wider column and the
   other three stack beside it; on a phone they run in order under the room.
   No box around any of it: the region's hairline above is the only rule. */
.pgharb ~ .lead--scene{ margin-top:var(--space-5); }
.pgsetup{ display:grid; grid-template-columns:minmax(0, 1fr); gap:var(--space-4); }
@media (min-width: 700px){
  .pgsetup{ grid-template-columns:minmax(0, 7fr) minmax(0, 5fr); align-items:start; }
}
.pgsetup-room{ min-width:0; }
.pgsetup-side{ display:flex; flex-direction:column; gap:var(--space-4); min-width:0; }

/* The axis gutter is the one measurement that does not scale with the type,
   so it gives a little where the screen is narrowest. The stat value needs
   nothing here: its clamp already does this continuously. */
@media (max-width: 380px){
  .pgweek-grid{ left:30px; }
  .pgweek-bars{ padding-left:30px; }
}
/* =========================================================================
   RESCUE MODE.

   One line and one button, left-aligned, which is the empty-state primitive
   Task 3.5 settled on and the right shape for a door that has to be readable
   at a glance on a bad day.

   Two placements, one component. Quiet, it sits in a region on the day's
   column and the region draws the rule and the heading, so the block itself
   is layout and nothing else. Surfaced, when the deadline is inside 48 hours,
   it stands on its own above the headline and takes .mode-hint's treatment: a
   hairline band on one edge, no fill, no tint, no accent. That is rule 1 of
   the design system, and it is also why the primary below it is still the one
   filled thing on the screen, which UI-SPEC section 5 requires.

   No colour says "urgent" here. The sentence already names the deadline and
   the hours; a red strip would be the product grading the reader for the
   state of their week, which is the voice rule this file is not going to be
   the first to break.
   ========================================================================= */
.rescue{
  text-align: left;
  max-width: 46ch;
}
.rescue-line{
  margin: 0 0 var(--space-3);
  color: var(--ink-mute);
  font-size: var(--fs-3);
  line-height: 1.5;
}
.rescue--due{
  padding-left: var(--space-4);
  border-left: var(--band-w) solid var(--line-strong);
  margin-bottom: var(--space-5);
}
/* =========================================================================
   THE COMEBACK SCREEN.

   One rule, and the reason there is only one is that this screen is Now's own
   composition with the queue taken out. The hero, the headline and the two
   stacked actions are .harbour--hero, .nowline and .nowacts from
   css/screens/now.css, unchanged and unduplicated: it renders under
   #main[data-screen="now"], so the full-bleed hero and the filled Sail-now
   treatment apply to it by construction rather than by a second copy of a
   dozen declarations. The one edit that went to that file instead of this one
   is a widening rather than an addition: .nowacts's second action was
   selected as .sailwith.btn--tertiary and is selected as .btn--tertiary now,
   which is what "the second action on Now is full width" always meant. So the
   way past this screen carries no class of its own, deliberately: a class in
   the markup with no rule behind it is what test/audit.js exists to catch.

   So what is left is the line under the headline. It is the return line, and
   it is prose rather than a number, which is why it is --ink-mute at --fs-3
   and measured: the headline above it is the fact and this is the sentence.
   The same three declarations rescue.css gives .rescue-line, and they are not
   shared, because two screens agreeing on how a quiet sentence is set is not
   the same as one component.

   No colour, no fill and no band anywhere here. The offer is the one filled
   thing on the screen, which is UI-SPEC section 5's rule for Now and is also
   the whole voice of this screen: nothing on it grades anybody for having
   been away. There is no dark-theme block and no reduced-motion block,
   because nothing here reads a scoped token and nothing here moves.
   ========================================================================= */
.comeback-line{
  margin: 0 0 var(--space-5);
  max-width: 46ch;
  color: var(--ink-mute);
  font-size: var(--fs-3);
  line-height: 1.5;
}
/* =========================================================================
   THE DEMO BAR.

   One sentence, one primary, one dismiss, on every screen. It sits where the
   email bar sits and carries the same dismiss control, because the frame
   already paints one persistent notice into the top of #main and a second one
   in a different place would be a second grammar for the same idea.

   No fill. Rule 1 of the design system is that separation is a hairline on
   one edge and never a fill under content, and a notice is still content. So
   it takes the treatment Rescue Mode's surfaced door already has: a band in
   --line-strong on the leading edge and nothing behind the words. That is
   also what keeps the primary inside it the one filled thing on the screen.

   Not in the accent and not in --danger. tokens.css rule 2 allows one accent
   field per screen and the screen under this already has one; and nothing
   about a demo is a warning. The button keeps the accent, because it is the
   only thing on the bar to press and the one action the page exists for.
   ========================================================================= */
.demobar{
  position:relative;
  display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-4);
  padding:var(--space-1) calc(var(--space-4) + 28px) var(--space-1) var(--space-4);
  border-left:var(--band-w) solid var(--line-strong);
  margin:0 0 var(--space-5);
}
.demobar-line{
  flex:1 1 22ch; margin:0;
  font-size:var(--fs-2); color:var(--ink-soft); line-height:1.5;
}
.demobar-acts{ flex:0 0 auto; }
.demobar-acts .btn{ margin:0; }
/* The same dismiss control the email bar carries, and the same three states.
   Restated rather than shared because .emailnag-x is scoped to that bar by
   name and widening its selector would make one component out of two notices
   that have nothing else in common. */
.demobar-x{
  position:absolute; top:var(--space-2); right:var(--space-2);
  width:28px; height:28px; display:grid; place-items:center;
  background:transparent; border:0; border-radius:50%; color:var(--ink-mute); cursor:pointer;
}
.demobar-x svg{ width:14px; height:14px; }
.demobar-x:active{ background:var(--surface); color:var(--ink); }
@media (hover:hover) and (pointer:fine){ .demobar-x:hover{ background:var(--surface); color:var(--ink); } }
.demobar-x:focus-visible{ box-shadow:var(--focus-ring); outline:none; }

/* =========================================================================
   THE WALKTHROUGH. Decision D46.

   On the app, a frame over everything with the demo playing itself inside it.
   Inside the frame, the caption bar: one sentence about what is on screen,
   where the step is, and Close, Back, Pause and Next. It is a dialog floating
   over the app, so it takes the sheet's real shadow; nothing else here does.
   ========================================================================= */
.tourscrim{ background:var(--ground); }
.tourframe{ position:fixed; inset:0; z-index:var(--z-scrim); }
.tourframe iframe{ display:block; width:100%; height:100%; border:0; }
.tourbar{
  position:fixed; left:var(--space-3); right:var(--space-3);
  bottom:calc(var(--safe-bottom) + var(--space-3));
  z-index:calc(var(--z-scrim) + 20);
  max-width:560px; margin:0 auto; padding:var(--space-4);
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-card);
  box-shadow:var(--shadow-3);
}
.tourbar-t{ margin:0 0 var(--space-3); padding-right:28px; font-size:var(--fs-3); line-height:1.45; color:var(--ink); }
/* One row at phone width: where the step is, then Back, Pause and Next, with
   Close as the corner control every sheet already carries. */
.tourbar-row{ display:flex; align-items:center; gap:var(--space-2); }
.tourbar-n{ flex:1 1 auto; font-size:var(--fs-2); color:var(--ink-mute); white-space:nowrap; }
.tourbar-row .btn{ flex:0 0 auto; width:auto; margin:0; padding-inline:var(--space-3); }
.tourbar-x{
  position:absolute; top:var(--space-2); right:var(--space-2);
  width:28px; height:28px; display:grid; place-items:center;
  background:transparent; border:0; border-radius:50%; color:var(--ink-mute); cursor:pointer;
}
.tourbar-x svg{ width:14px; height:14px; }
.tourbar-x:active{ background:var(--surface-2); color:var(--ink); }
@media (hover:hover) and (pointer:fine){ .tourbar-x:hover{ background:var(--surface-2); color:var(--ink); } }
.tourbar-x:focus-visible{ box-shadow:var(--focus-ring); outline:none; }
/* =========================================================================
   THE REDESIGN, IN THE APP.

   Behaviour, drawn in the app's own system: the same tokens, the same rules,
   the same radii. What changes is what the screens say and how much weight
   each choice is given.
   ========================================================================= */

/* Now: Start and Not now, side by side, the same size and the same weight.
   Refusing is a legitimate choice (D74), so it is not drawn as the lesser
   one. Sail now stays the one filled control on the screen. */
.nowpick{ margin:0 0 var(--space-5); }
.nowpick-acts{
  display:grid; grid-template-columns:1fr 1fr; gap:var(--space-3);
  margin-top:var(--space-4);
}
.nowpick-acts .btn{ width:100%; min-height:48px; }   /* D130: 48px for the two choices */

/* "Why this task?", inline: a text control that opens the reasons in place,
   under the row, with nothing else moving. */
.nowpick-whybtn{
  display:inline-flex; align-items:center; gap:var(--space-1);
  min-height:44px; padding:0; margin-top:var(--space-2);
  background:transparent; border:0; color:var(--ink-soft);
  font:inherit; font-size:var(--fs-2); text-decoration:underline;
  text-underline-offset:3px; cursor:pointer;
}
.nowpick-whybtn:focus-visible{ box-shadow:var(--focus-ring); outline:none; border-radius:var(--r-control); }
/* The first reason, always shown under the row (D130). */
.nowpick-reason{ margin:var(--space-2) 0 0; color:var(--ink-soft); font-size:var(--fs-2); line-height:1.5; }
.nowpick-reasons{
  margin:0 0 var(--space-2); padding:0 0 0 var(--space-4);
  color:var(--ink-soft); font-size:var(--fs-2); line-height:1.5;
}
.nowpick-reasons li{ margin:var(--space-1) 0; }
.nowpick-reasons[hidden]{ display:none; }

/* The session: the time left and the task, and the controls. */
.sess-time{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:var(--space-2); margin:var(--space-6) 0 var(--space-5);
}
.sess-time .timer-sub{ color:var(--ink-mute); font-size:var(--fs-2); }
/* Time you can see (18 October): a thin bar that shrinks at the session's
   own scale, and the next fixed thing in words. No ring (D74). */
.sess-bar{ width:min(18rem, 70vw); height:4px; margin:var(--space-3) auto 0; background:var(--line-soft); border-radius:var(--r-pill); overflow:hidden; }
.sess-bar i{ display:block; height:100%; width:100%; background:var(--ink-soft); border-radius:var(--r-pill); }
.sess-edge{ margin:var(--space-2) 0 0; font-size:var(--fs-2); color:var(--ink-mute); text-align:center; }

/* Paused: what is banked, said plainly, and the optional question. */
.sess-banked{
  margin:var(--space-3) 0 0; text-align:center;
  color:var(--ink); font-size:var(--fs-3);
}
.sess-why{ margin-top:var(--space-4); text-align:center; }
.sess-why-q{ color:var(--ink-soft); font-size:var(--fs-2); margin:0 0 var(--space-2); }

/* Warnings are the attention colour, never red. The crisis control keeps
   --bad and is the only red the app draws; --danger is everything else, and
   under the redesign it is the shared palette's attention colour
   (colors.css, D80), which holds 4.5:1 as text in both themes where --warn
   does not. */
:root[data-redesign]{ --danger: var(--nv-attention); }

/* The redesign's first run (flags.onboarding2). Rules, not boxes: a fixed
   commitment is a row under a hairline, the hour choices are three large
   controls, and the week is a list. */
#ob2root textarea{ width:100%; font:inherit; font-size:var(--fs-3); padding:var(--space-3);
  border:1px solid var(--line); border-radius:var(--r-control); background:var(--ground); color:var(--ink); }
.ob2-cards, .ob2-week{ list-style:none; margin:0; padding:0; }
.ob2-card{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-3) 0;
  border-bottom:1px solid var(--line-soft); }
.ob2-card-n{ flex:1; font-weight:var(--fw-med); color:var(--ink); }
.ob2-card-m{ color:var(--ink-soft); font-size:var(--fs-2); }
.ob2-empty{ color:var(--ink-soft); margin:0 0 var(--space-3); }
.ob2-add{ display:grid; grid-template-columns:1fr 1fr; gap:var(--space-3); margin:var(--space-4) 0; align-items:end; }
.ob2-add label{ display:flex; flex-direction:column; gap:var(--space-1); font-size:var(--fs-2); color:var(--ink-soft); }
.ob2-add input, .ob2-add select{ min-height:44px; font:inherit; padding:0 var(--space-2);
  border:1px solid var(--line); border-radius:var(--r-control); background:var(--ground); color:var(--ink); }
.ob2-hours{ display:grid; gap:var(--space-3); }
.ob2-hour{ display:flex; justify-content:space-between; align-items:center; min-height:64px;
  padding:0 var(--space-4); font:inherit; font-size:var(--fs-4); color:var(--ink); background:var(--ground);
  border:1px solid var(--line); border-radius:var(--r-control); cursor:pointer; }
.ob2-hour.is-on{ border-color:var(--ink); font-weight:var(--fw-med); }
.ob2-hour:focus-visible{ box-shadow:var(--focus-ring); outline:none; }
.ob2-goal{ color:var(--ink); margin:0 0 var(--space-3); }
.ob2-week li{ display:grid; grid-template-columns:1fr auto; gap:var(--space-1) var(--space-3);
  padding:var(--space-2) 0; border-bottom:1px solid var(--line-soft); }
.ob2-day{ color:var(--ink); }
.ob2-h{ color:var(--ink-soft); }
.ob2-around{ grid-column:1 / -1; color:var(--ink-mute); font-size:var(--fs-2); }
/* The step heading takes focus so a screen reader announces it; it is not a
   control, so it draws no ring. */
#ob2h:focus{ outline:none; }
/* A fixed session on now or about to start, on Now (plan/reminders.js): one
   line under a hairline, its name opening the session's sheet and one button
   starting a session on it. Not a card, and not the band: the suggestion
   under it keeps the screen's one accent. */
.nowfixed{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-3) 0;
  border-top:1px solid var(--line); margin-bottom:var(--space-3); }
.nowfixed-n{ flex:1; min-width:0; font-size:var(--fs-3); font-weight:var(--fw-med); color:var(--ink);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.nowfixed-go, .nowfixed-done{ flex:0 0 auto; }
.nowfixed{ flex-wrap:wrap; }
.nowfixed-n{ flex-basis:12rem; }
/* A choice of several moments, which wraps on a phone rather than squeezing
   seven buttons into one line. Each keeps its own edge once they wrap. */
.segrow.segrow--wrap{ flex-wrap:wrap; gap:var(--space-2); border:none; overflow:visible; }
.segrow.segrow--wrap > button{ flex:1 0 auto; border:1px solid var(--line); border-radius:var(--r-control); }
/* Your layout (js/ui/layout.js, 4 October). A section put away is not drawn;
   the attribute is set by the frame and by nothing else. */
#main [data-sec-off]{ display:none; }
.layout-presets{ display:flex; flex-direction:column; gap:var(--space-2); }
.layout-preset{ display:flex; flex-direction:column; align-items:flex-start; gap:2px; text-align:left;
  padding:var(--space-3); border:1px solid var(--line); border-radius:var(--r-control); background:none;
  color:var(--ink); font:inherit; cursor:pointer; min-height:44px; }
.layout-preset.on{ border-color:var(--ink); }
.layout-preset-t{ font-weight:var(--fw-med); font-size:var(--fs-3); }
.layout-preset-s{ font-size:var(--fs-2); color:var(--ink-mute); }
.layout-list{ list-style:none; margin:var(--space-3) 0 0; padding:0; border-top:1px solid var(--line); }
.layout-row{ display:flex; align-items:center; gap:var(--space-2); min-height:48px;
  border-bottom:1px solid var(--line); background:var(--ground); touch-action:none; }
.layout-row.is-off .layout-name{ color:var(--ink-mute); }
.layout-row.is-drag{ position:relative; z-index:2; box-shadow:var(--shadow-3); }
.layout-grip{ display:grid; place-items:center; width:32px; height:44px; color:var(--ink-mute); cursor:grab; flex:none; }
.layout-grip svg{ width:16px; height:16px; }
.layout-name{ flex:1; min-width:0; font-size:var(--fs-3); color:var(--ink); }
.layout-lock, .layout-state{ font-size:var(--fs-1); color:var(--ink-mute); }
.layout-mv{ display:grid; place-items:center; width:36px; height:44px; border:none; background:none;
  color:var(--ink-soft); cursor:pointer; flex:none; }
.layout-mv:disabled{ opacity:.3; }
.layout-mv svg{ width:18px; height:18px; }
.layout-list .layout-show{ display:grid; place-items:center; width:44px; height:44px; flex:none; margin:0; padding:0; }
.layout-show input{ width:20px; height:20px; accent-color:var(--ink); }
/* Planning and wellbeing (D136): the five-minute start under the one task,
   the line on the session screen once five minutes are done, and the
   check-ins, which reuse Rescue Mode's band and wrap on a phone. */
.nowfive{ margin-top:var(--space-3); }
.nowfive .fnote{ margin:0 0 var(--space-2); }
.nowfive .nowpick-five{ width:auto; }
.sess-five{ margin:var(--space-3) 0; text-align:center; }
.wbcard .segrow{ flex-wrap:wrap; }
.wbcard input[type="time"]{ max-width:9rem; }
.wbplan{ margin:var(--space-2) 0; padding-left:var(--space-5); }
.wbplan li{ margin:var(--space-1) 0; }
/* D147: under Priority a meal or a dose holds Now, in the suggestion's own band. */
.wbhold .segrow{ flex-wrap:wrap; margin-top:var(--space-2); }
.wbhold .fnote{ margin:var(--space-1) 0 0; }
/* While a session runs (13 October): Now shows the running one, not the next. */
.nowfocus-k{ margin:0; font-size:var(--fs-1); font-weight:var(--fw-hi); letter-spacing:.08em; text-transform:uppercase; color:var(--ink-soft); }
.nowfocus-n{ margin:var(--space-2) 0; font-size:var(--fs-5); font-weight:var(--fw-hi); line-height:1.2; color:var(--ink); }
.nowfocus .fnote{ margin:0 0 var(--space-3); }
.nowfocus-back{ width:auto; }
/* =========================================================================
   DAYBREAK (D132). Bright enough to start, calm enough to stay.

   One sunny card holds the task Sailyn picked; everything around it is soft:
   white or tinted surfaces on a warm ground, colour only as small dots and
   chips. Four hues, each with one job: marigold for starting and what has
   been earned, jade for plans and progress, lilac for rest and links, apricot
   for a shortfall and never for a missed day. Atkinson Hyperlegible Next for
   reading, Bricolage Grotesque for headlines and task names, Atkinson
   Hyperlegible Mono for numbers that change. Rounder is calmer: every button,
   input and tab is a pill, cards are 24, the Now card 32. One curve, rise and
   never bounce.

   This reverses, for the app, the rules of the instrument it replaces: there
   are cards now, radii above 3px, a fifth weight, and colour beyond one
   band. DECISIONS.md D132 says why each was changed. Colours are the brief's,
   converted to oklch and measured: every text pairing holds 4.5:1 or better
   in both themes (the brief's caption grey did not, at 3.8:1, so --ink-mute
   is darker than its swatch).
   ========================================================================= */
/* ---- type ---- */
:root[data-ui="daybreak"] body{ font-family: var(--font-ui); background: var(--ground); }
:root[data-ui="daybreak"][data-theme="dark"]{ color-scheme: dark; }
:root[data-ui="daybreak"]:not([data-theme="dark"]){ color-scheme: light; }
/* The scenery clock's accent (lib/sky.js): a band of the sky as it is now at
   the top of every screen, from daybreak to midnight, whatever the theme. It
   is the one ambient background the system allows, it sits under content,
   and at 14 per cent it moves no text below its contrast floor in either
   theme (test/daybreakcheck.js measures it at noon and at midnight). More
   contrast and forced colours take it away. */
:root[data-ui="daybreak"]:not([data-contrast="more"]) body{
  background: linear-gradient(to bottom, color-mix(in oklch, var(--scene-sky, var(--ground)) 14%, var(--ground)), var(--ground) 180px) no-repeat, var(--ground);
}
:root[data-ui="daybreak"] .screen-title,
:root[data-ui="daybreak"] .nowline,
:root[data-ui="daybreak"] .sess-name,
:root[data-ui="daybreak"] .onboard h2,
:root[data-ui="daybreak"] .sheet h2{
  font-family: var(--font-head); font-weight: var(--fw-head); letter-spacing: -0.025em;
}
:root[data-ui="daybreak"] .region-title{ font-weight: var(--fw-hi); letter-spacing: 0.09em; }
:root[data-ui="daybreak"] a:not(.btn):not(.setup-open){ color: var(--lilac-text); }

/* ---- controls: pills, 48px ---- */
:root[data-ui="daybreak"] .btn{ min-height: 48px; padding: var(--space-3) var(--space-5); font-weight: var(--fw-hi); }
:root[data-ui="daybreak"] .btn--secondary{ background: var(--surface-2); }
:root[data-ui="daybreak"] .btn--tertiary{ color: var(--lilac-text); }
:root[data-ui="daybreak"] .btn .ic{ width: 20px; height: 20px; }
:root[data-ui="daybreak"] input[type="text"], :root[data-ui="daybreak"] input[type="number"],
:root[data-ui="daybreak"] input[type="email"], :root[data-ui="daybreak"] input[type="password"],
:root[data-ui="daybreak"] input[type="time"], :root[data-ui="daybreak"] input[type="date"],
:root[data-ui="daybreak"] select{
  border-radius: var(--r-control); border-color: var(--line-strong); background: var(--surface);
}
:root[data-ui="daybreak"] textarea{ border-radius: var(--radius-s); border-color: var(--line-strong); background: var(--surface); }

/* ---- the one sunny card: the task Sailyn picked ---- */
:root[data-ui="daybreak"] .nowpick{
  /* clip, not hidden: a box that hides its overflow is a scroll container,
     and a flex or grid item that is one may shrink to its header. */
  position: relative; overflow: clip; isolation: isolate; flex-shrink: 0;
  background: var(--sun); color: var(--sun-ink);
  border-radius: var(--r-now); padding: var(--space-5);
  margin: 0 0 var(--space-5);
}
/* One sun in the corner, clipped by the card and kept off the words and the
   buttons. Its light is the scenery clock's: warm white by day, silver at
   night. More contrast and forced colours take it away. */
:root[data-ui="daybreak"] .nowpick::before{
  content: ""; position: absolute; z-index: -1; border-radius: var(--r-circle); pointer-events: none;
  width: 150px; height: 150px; right: -64px; top: -72px;
  background: color-mix(in oklch, var(--scene-glow, var(--sun-face)) 28%, transparent);
}
:root[data-ui="daybreak"] .nowpick .region-head{ border: 0; padding: 0; margin: 0 0 var(--space-2); }
:root[data-ui="daybreak"] .nowpick .region-title{ color: var(--sun-ink); display: inline-flex; align-items: center; gap: var(--space-2); }
:root[data-ui="daybreak"] #main .nowsug.is-now.nowpick .nowrow{ border: 0; padding: 0; background: none; }
:root[data-ui="daybreak"] .nowpick .nowrow-title{
  font-family: var(--font-head); font-weight: var(--fw-head); font-size: 1.75rem; line-height: 1.05;
  letter-spacing: -0.025em; color: var(--sun-ink);
}
/* Everything on the card is the card's own ink, never the theme's: in the dark
   theme the ink is light, and light on marigold is unreadable. */
:root[data-ui="daybreak"] .nowpick .nowrow-meta,
:root[data-ui="daybreak"] .nowpick .nowrow-len,
:root[data-ui="daybreak"] .nowpick .nowrow-dur{ color: var(--sun-ink); }
:root[data-ui="daybreak"] .nowpick .nowrow-title{ overflow-wrap: anywhere; hyphens: auto; min-width: 0; }
:root[data-ui="daybreak"] .nowpick .nowrow-dur{ flex: 0 0 auto; align-self: flex-start; margin-top: 6px; }
:root[data-ui="daybreak"] .nowpick-reason{
  display: flex; gap: var(--space-2); align-items: flex-start;
  color: var(--sun-ink); font-size: var(--fs-3); margin: var(--space-3) 0 0;
}
:root[data-ui="daybreak"] .nowpick-reason .ic{ flex: 0 0 auto; margin-top: 2px; }
:root[data-ui="daybreak"] .nowpick-whybtn{ color: var(--sun-ink); }
:root[data-ui="daybreak"] .nowpick-reasons{ color: var(--sun-ink); }
/* Start leads and Not now stays beside it at the same height: refusing is
   still a choice on the card, it is just not the one the card was made for. */
:root[data-ui="daybreak"] .nowpick-acts{ grid-template-columns: 1.7fr 1fr; margin-top: var(--space-4); }
:root[data-ui="daybreak"] .nowpick-acts .btn{ min-height: 52px; font-size: var(--fs-4); white-space: nowrap; padding-inline: var(--space-3); }
/* The live band's accent edge has no place on a card that is already the
   screen's one bright thing: the rule above takes it off. */
:root[data-ui="daybreak"] .nowpick-start{ background: var(--sun-ink); color: var(--sun-face); }
/* Not now is quieter: an outline rather than a fill, still a full target. */
:root[data-ui="daybreak"] .nowpick-later{ background: transparent; color: var(--sun-ink);
  box-shadow: inset 0 0 0 2px var(--sun-ink-line); }
:root[data-ui="daybreak"] .nowpick .nowrow-check{ border-color: var(--sun-ink); }

/* ---- status chips under the day's line ---- */
:root[data-ui="daybreak"] .nowchips{ display: flex; flex-wrap: wrap; gap: var(--space-2); margin: calc(-1 * var(--space-2)) 0 var(--space-4); }
.dchip{ display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; border-radius: var(--r-pill);
  font-size: var(--fs-2); font-weight: var(--fw-hi); line-height: 1.4; background: var(--surface-2); color: var(--ink-soft); }
.dchip .ic{ width: 14px; height: 14px; }
.dchip--jade{ background: var(--jade-tint); color: var(--jade-text); }
.dchip--sun{ background: var(--sun-tint); color: var(--sun-text); }
.dchip--apricot{ background: var(--apricot-tint); color: var(--apricot-text); }
.dchip--lilac{ background: var(--lilac-tint); color: var(--lilac-text); }
.dchip .num{ font-family: var(--font-num); font-variant-numeric: tabular-nums; }

/* ---- the week of suns ---- */
:root[data-ui="daybreak"] .nowweek{ background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: var(--space-4); margin: 0 0 var(--space-5); }
:root[data-ui="daybreak"] .nowweek-days{ width: 100%; justify-content: space-between; }
:root[data-ui="daybreak"] .nowweek-d{
  min-width: 0; width: 34px; height: auto; border: 0; display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: none; color: var(--ink-mute); font-weight: var(--fw-hi);
}
/* Three shapes, not three colours: an open ring for a day not yet sailed, a
   filled sun with its halo for a day that was, and a dashed ring for today. */
:root[data-ui="daybreak"] .nowweek-d::before{
  content: ""; width: 28px; height: 28px; border-radius: var(--r-circle); background: none;
  border: 2px solid var(--line-strong);
  transition: background var(--dur-slow) var(--ease), outline-color var(--dur-slow) var(--ease);
}
:root[data-ui="daybreak"] .nowweek-d.is-lit{ background: none; color: var(--ink); }
:root[data-ui="daybreak"] .nowweek-d.is-lit::before{ width: 32px; height: 32px; border: 0; background: var(--sun); outline: 5px solid var(--sun-tint); }
:root[data-ui="daybreak"] .nowweek-d.is-today{ color: var(--ink); }
:root[data-ui="daybreak"] .nowweek-d.is-today:not(.is-lit)::before{ background: none; border: 2px dashed var(--jade-text); width: 28px; height: 28px; }
:root[data-ui="daybreak"] .nowweek-today{ display: flex; align-items: baseline; gap: var(--space-2); }

/* ---- Sail now: a pill in the harbour's own navy, with its boat ---- */
:root[data-ui="daybreak"] [data-surface="harbour"].nowsail{ border-radius: var(--r-control); min-height: 56px; font-weight: var(--fw-hi); }
:root[data-ui="daybreak"] .nowacts .nowsail .ic{ width: 22px; height: 22px; }

/* ---- soft everywhere else: a region is a white card on the warm ground ---- */
:root[data-ui="daybreak"] #main .region{
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: var(--space-4) var(--space-4) var(--space-3); margin-bottom: var(--space-4);
}
:root[data-ui="daybreak"] #main .region .region{ background: none; border: 0; padding: 0; margin: 0; border-radius: 0; }
:root[data-ui="daybreak"] #main .region .region-head{ border-top: 0; padding-top: 0; }
:root[data-ui="daybreak"] #main .nowpick.region, :root[data-ui="daybreak"] #main .nowpick .region{ background: none; border: 0; padding: 0; margin: 0; }

/* The groups on Plan and Tasks are cards too. */
:root[data-ui="daybreak"] #main .pgroup{
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: var(--space-2) var(--space-4); margin-bottom: var(--space-4); flex-shrink: 0;
}
:root[data-ui="daybreak"] #main .pgroup .prow:last-child{ border-bottom: 0; }

/* The usual trip, in Settings, You: a number and its unit on one line. */
.errand-trip{ display: flex; align-items: center; gap: var(--space-2); }

/* ---- warm warnings, no red ---- */
:root[data-ui="daybreak"] .riskbox{
  display: flex; flex-direction: column; gap: var(--space-2);
  border: 0; border-radius: var(--radius-s); background: var(--apricot-tint); color: var(--ink);
  padding: var(--space-3) var(--space-4);
}
:root[data-ui="daybreak"] .riskbox b{ color: var(--apricot-text); }

/* ---- the header over the harbour: what has been earned in the lamp's own
   light, because the instrument gold is a daytime colour on a night picture ---- */
:root[data-ui="daybreak"] #app header[data-surface="harbour"]:not([data-past]) .hdrcoins{ color: var(--lamp); }

/* ---- the tab bar: a soft pill bar, the current tab a dark pill ---- */
@media (max-width: 879.98px){
:root[data-ui="daybreak"] #app nav{
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); box-shadow: none;
  margin: 0 calc(var(--space-3) + 64px) max(var(--space-2), var(--safe-bottom)) var(--space-3); padding: 6px;
}
/* The plus docks beside the tab bar, in the row the bar already keeps clear,
   so it never sits over a button on the screen above it (the theme merge
   plan: no hidden action). Centred on the bar's 72px. */
:root[data-ui="daybreak"] .pfab,
:root[data-ui="daybreak"] body[data-sessmini] .pfab{
  right: var(--space-3); bottom: calc(max(var(--space-2), var(--safe-bottom)) + 8px);
  width: 56px; height: 56px;
  z-index: var(--z-nav);   /* level with the bar, above the ground band under it */
}
:root[data-ui="daybreak"] .pfab svg{ width: 20px; height: 20px; }
:root[data-ui="daybreak"] #app nav button{ border-radius: var(--r-pill); color: var(--ink-soft); padding: var(--space-2) 0; }
:root[data-ui="daybreak"] #app nav button svg{ width: 22px; height: 22px; }
:root[data-ui="daybreak"] #app nav[data-four] button.on{ background: var(--ink); color: var(--surface); }
:root[data-ui="daybreak"] #app nav[data-four] button.on svg,
:root[data-ui="daybreak"] #app nav[data-four] button.on span{ color: var(--surface); }
:root[data-ui="daybreak"] #app nav button span{ font-size: 0.75rem; font-weight: var(--fw-hi); }
}
/* From 880 the bar is the rail; its current item is the same dark pill. */
@media (min-width: 880px){
  :root[data-ui="daybreak"] #app nav button{ border-radius: var(--r-pill); }
  :root[data-ui="daybreak"] #app nav[data-four] button.on{ background: var(--ink); color: var(--surface); box-shadow: none; }
  :root[data-ui="daybreak"] #app nav[data-four] button.on svg,
  :root[data-ui="daybreak"] #app nav[data-four] button.on span{ color: var(--surface); }
}

/* Now from 880, in Daybreak's order: the line, how the plan stands, the card,
   the week, then Sail now, beside the harbour. */
@media (min-width: 880px){
  :root[data-ui="daybreak"] #main[data-screen="now"] > .harbour--hero{ grid-row: 1 / span 5; }
  :root[data-ui="daybreak"] #main[data-screen="now"] > .nowchips{ grid-column: 2; grid-row: 2; }
  :root[data-ui="daybreak"] #main[data-screen="now"] > .nowsug{ grid-column: 2; grid-row: 3; }
  :root[data-ui="daybreak"] #main[data-screen="now"] > .nowweek{ grid-column: 2; grid-row: 4; }
  :root[data-ui="daybreak"] #main[data-screen="now"] > .nowacts{ grid-column: 2; grid-row: 5; }
}

/* ---- the session ---- */
:root[data-ui="daybreak"] .sess-time .timer{ font-family: var(--font-num); font-weight: var(--fw-med); letter-spacing: 0; }
:root[data-ui="daybreak"] .sess-time .timer-sub{ font-family: var(--font-num); color: var(--ink-mute); }
:root[data-ui="daybreak"] .sess-actions button{ border-radius: var(--r-control); display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); }
:root[data-ui="daybreak"] .sess-actions button .ic{ width: 20px; height: 20px; }
:root[data-ui="daybreak"] .sess-shut-list li{ background: var(--surface-2); border-color: transparent; font-weight: var(--fw-hi); }
:root[data-ui="daybreak"] .sess-focus{ display: inline-flex; align-self: center; }

/* ---- a break is lilac ---- */
:root[data-ui="daybreak"] #brkroot{ background: var(--lilac-tint); }
:root[data-ui="daybreak"] #brkroot .sess-cat{ color: var(--lilac-text); font-weight: var(--fw-hi); letter-spacing: 0.09em; }

/* ---- sheets ---- */
:root[data-ui="daybreak"] .sheet, :root[data-ui="daybreak"] .onboard{ border-radius: var(--r-card) var(--r-card) 0 0; }
:root[data-ui="daybreak"] .onboard{ border-radius: var(--r-card); }

/* ---- More contrast (D133): stronger edges, no decoration behind text ---- */
:root[data-ui="daybreak"][data-contrast="more"] .nowpick::before{ display: none; }
:root[data-ui="daybreak"][data-contrast="more"] input,
:root[data-ui="daybreak"][data-contrast="more"] select,
:root[data-ui="daybreak"][data-contrast="more"] textarea{ border-width: 2px; }
:root[data-ui="daybreak"][data-contrast="more"] .btn--secondary{ box-shadow: inset 0 0 0 2px var(--line-strong); }
:root[data-ui="daybreak"][data-contrast="more"] #main .region,
:root[data-ui="daybreak"][data-contrast="more"] #main .pgroup,
:root[data-ui="daybreak"][data-contrast="more"] .nowweek{ border-width: 2px; }
:root[data-ui="daybreak"][data-contrast="more"]{ --focus-ring: 0 0 0 3px var(--ground), 0 0 0 5px var(--lilac-text); }

/* ---- forced colours: the system's palette, and nothing decorative ---- */
@media (forced-colors: active){
  :root[data-ui="daybreak"] body{ background: Canvas; }
  :root[data-ui="daybreak"] .nowpick::before{ display: none; }
  :root[data-ui="daybreak"] .nowpick{ border: 2px solid CanvasText; }
  :root[data-ui="daybreak"] .nowweek-d::before{ border: 2px solid CanvasText; }
  :root[data-ui="daybreak"] .nowweek-d.is-lit::before{ background: Highlight; outline: 0; }
}
