/* v3.2 — the wide Today (views/today.js, ui/todayWide.js) + the progress readouts
   that replaced the bar on Today: Lok's inked greeting, Zi Xuan's lit garland + roses.
   Layout rules apply from 1100 px (WIDE_MQ); the progress readouts apply at every width. */

@property --p{syntax:'<percentage>';inherits:true;initial-value:0%}

/* ── Progress caption (both people, every width) ─────────────────────────── */
.pcap{margin:0;font:500 13px/1.4 var(--f-ui);color:var(--muted)}
.pcap b{color:var(--ink);font-weight:600}
.pcap .pnote{color:var(--accent)}
.xuan .pcap{font-size:14px}

/* ── Lok: the greeting fills with ink (every width) ──────────────────────── */
.app{--ink-fill:var(--ink);--ink-edge:oklch(.38 .12 265)}
.app.dark{--ink-edge:oklch(.85 .08 265)}
.inkh{position:relative;display:block;width:fit-content;max-width:100%;white-space:nowrap;font-size:clamp(28px,8.6vw,40px);   /* --p measures the words, not the empty box */
  color:color-mix(in oklch,var(--ink) 16%,var(--bg))}
.inkh .inkwet{position:absolute;inset:0;color:transparent;pointer-events:none;
  background:linear-gradient(90deg,var(--ink-fill) 0 calc(var(--p) - 5%),var(--ink-edge) calc(var(--p) - 1%),var(--ink-fill) var(--p));
  -webkit-background-clip:text;background-clip:text;
  -webkit-mask-image:linear-gradient(90deg,#000 calc(var(--p) - 1.5%),transparent calc(var(--p) + 1.5%));
  mask-image:linear-gradient(90deg,#000 calc(var(--p) - 1.5%),transparent calc(var(--p) + 1.5%))}   /* no SVG filter: it rasterised the glyphs and broke their edges */
.inkh .inkfx{position:absolute;inset:0;pointer-events:none}       /* progressFx sparks land at the ink edge */
.inkh[data-tier="3"]{--ink-edge:oklch(.72 .13 80)}                  /* nearly there: a gold edge */
.inkh[data-tier="4"] .inkwet{-webkit-mask-image:none;mask-image:none;
  background:linear-gradient(100deg,var(--ink-fill) 0 40%,var(--ink-edge) 50%,var(--ink-fill) 60% 100%);background-size:250% 100%}
@media (prefers-reduced-motion:no-preference){
  .inkh{transition:--p calc(1100ms * var(--motion)) var(--ease-spring)}
  .inkh[data-tier="4"] .inkwet{animation:inkgloss calc(4s * var(--motion)) ease-in-out infinite}
  .inkh.bump .inkwet{animation:inkbump calc(520ms * var(--motion)) var(--ease-decel)}
}
@keyframes inkgloss{from{background-position:100% 0}to{background-position:0 0}}
@keyframes inkbump{40%{filter:brightness(1.35)}}

/* ── The progress line (both people, every width): evident at a glance, exact on hover ── */
.meter{position:relative;display:block;max-width:520px;margin-top:14px;padding:8px 0;outline:none;cursor:default}
.mtrack{position:relative;display:block;height:6px;border-radius:3px;background:color-mix(in oklch,var(--ink) 11%,transparent)}
.mfill{position:absolute;left:0;top:0;bottom:0;width:var(--p);border-radius:inherit;background:var(--ink-fill)}
.mfill::after{content:"";position:absolute;right:-6px;top:50%;width:12px;height:12px;margin-top:-6px;border-radius:50%;
  background:var(--ink-fill);box-shadow:0 1px 3px oklch(0 0 0 / .25)}
/* Lok: a clean ink rule — solid fill, crisp ends */
.inkrule .mtrack{height:5px;border-radius:999px;background:color-mix(in oklch,var(--ink) 9%,transparent);box-shadow:inset 0 0 0 1px color-mix(in oklch,var(--ink) 6%,transparent)}
.inkrule .mfill{background:linear-gradient(90deg,color-mix(in oklch,var(--ink-fill) 82%,var(--ink-edge)),var(--ink-fill))}
.inkrule .mfill::after{background:var(--surface);box-shadow:inset 0 0 0 3px var(--ink-fill),0 1px 3px oklch(0 0 0 / .2)}
.inkrule[data-tier="3"] .mfill::after{box-shadow:inset 0 0 0 3px oklch(.72 .13 80),0 1px 3px oklch(0 0 0 / .2)}
.meter[data-tier="4"] .mfill::after{background:oklch(.78 .14 80);box-shadow:0 0 0 3px oklch(.9 .1 85 / .5)}
.meter:focus-visible .mtrack{outline:2px solid var(--accent);outline-offset:4px}
.meter::after{content:attr(data-tip);position:absolute;left:clamp(70px,var(--p),calc(100% - 70px));bottom:calc(100% - 2px);z-index:5;
  transform:translate(-50%,4px);opacity:0;pointer-events:none;white-space:nowrap;padding:5px 10px;border-radius:8px;
  background:var(--ink);color:var(--surface);font:600 12px/1.2 var(--f-ui);box-shadow:0 6px 18px oklch(0 0 0 / .18)}
.meter:hover::after,.meter:focus-visible::after{opacity:1;transform:translate(-50%,0)}
@media (prefers-reduced-motion:no-preference){
  .mfill{transition:width calc(1100ms * var(--motion)) var(--ease-spring)}
  .meter::after{transition:opacity var(--dur-fast) var(--ease-standard),transform var(--dur-fast) var(--ease-standard)}
}
/* Zi Xuan: a stem that grows toward her rose; leaf-ticks mark the bloom stages */
.roseline{--rh:39px;display:flex;align-items:flex-end;max-width:min(580px,100%);padding:0}   /* --rh = half the rose: the track runs under it to the stem */
.roseline .mtrack{flex:1;height:4px;margin:0 calc(-1 * var(--rh)) 3px 0;background:oklch(.32 .03 330)}
.roseline .mfill{background:linear-gradient(90deg,oklch(.55 .08 150),oklch(.74 .14 5));box-shadow:0 0 10px oklch(.74 .14 5 / .45)}
.roseline .mfill::after{background:oklch(.86 .09 5);box-shadow:0 0 12px oklch(.8 .12 5 / .8)}
.roseline[data-tier="4"] .mfill{background:linear-gradient(90deg,oklch(.62 .09 150),oklch(.85 .13 85))}
.mtick{position:absolute;top:50%;width:7px;height:7px;margin:-3.5px 0 0 -3.5px;transform:rotate(45deg);border-radius:1px 4px;background:oklch(.38 .03 330)}
.mtick.on{background:oklch(.66 .1 150)}
.roseline::after{left:clamp(70px,calc((100% - var(--rh)) * var(--pn) / 100),calc(100% - 70px));bottom:calc(100% - 22px);background:oklch(.93 .04 350);color:oklch(.22 .04 340)}

/* The rose (ui/rose.js): five illustrated bloom frames (bud → full) stacked on one shared
   stem base. --o (0–1, registered so it glides) picks the frame: frame i appears at its bloom
   tier (25·i %, full bloom only at 100) with a short crossfade, and the whole rose grows a
   little between tiers, so it keeps visibly opening as progress rises. */
@property --o{syntax:'<number>';inherits:true;initial-value:0}
.roseart{position:relative;flex:none;display:block;width:78px;height:91px}
.roseart .rgrow,.roseart .rhead{position:absolute;inset:0;transform-origin:50% 97%}
.roseart .rgrow{transform:scale(calc(.9 + var(--o) * .1))}
.roseart img{position:absolute;inset:0;width:100%;height:100%;user-select:none;-webkit-user-drag:none;
  opacity:clamp(0,min((var(--o) * 100 - var(--i) * 25 + 4) / 4,(var(--i) * 25 + 25 - var(--o) * 100) * 4),1)}
.roseart .rspark{position:absolute;inset:0;width:100%;height:100%;overflow:visible;opacity:clamp(0,(var(--o) - .98) * 50,1)}
.roseart .rspark *{transform-box:fill-box;transform-origin:center}
@media (prefers-reduced-motion:no-preference){
  .roseart{transition:--o calc(1400ms * var(--motion)) var(--ease-spring)}
  .roseart .rhead{animation:rsway calc(5s * var(--motion)) ease-in-out infinite alternate}
  .roseart.bloom .rhead{animation:rbloom calc(800ms * var(--motion)) var(--ease-spring) both}
  .roseart .rspark *{animation:rtwinkle calc(2.4s * var(--motion)) ease-in-out infinite alternate}
  .roseart .rspark :nth-child(2n){animation-delay:calc(-1.2s * var(--motion))}
}
@keyframes rsway{from{transform:rotate(-2.5deg)}to{transform:rotate(2.5deg)}}
@keyframes rbloom{0%{transform:scale(1)}40%{transform:scale(1.14)}100%{transform:scale(1)}}
@keyframes rtwinkle{from{transform:scale(.7);opacity:.6}to{transform:scale(1.1);opacity:1}}
@media (min-width:1100px){ .roseart{width:104px;height:122px} .roseline{--rh:52px} }

/* ── Zi Xuan: the garland is the progress bar (every width) ──────────────── */
.lights .bulb .glass{transform-box:fill-box;transform-origin:center}
.lights .glass,.lights .halo,.lights .glint{transition:opacity calc(700ms * var(--motion)) var(--ease-decel)}
.lights .bulb.off .halo{opacity:0}
.lights .bulb.off .glass{fill:oklch(.3 .03 330);opacity:.85}
.lights .bulb.off .glint{opacity:.2}
.lights .bulb.off,.lights .bulb.off .glass,.lights .bulb.off .halo,.lights .bulb.off .glint{animation:none}
.lights.gold .bulb .glass{fill:url(#lbGold)}
.lights.gold .bulb .halo{fill:url(#lhGold)}
.xuan *{font-variant-numeric:lining-nums}                         /* Cormorant's old-style 1 reads as I */
@media (prefers-reduced-motion:no-preference){
  .lights .bulb.justlit .glass{animation:lit calc(900ms * var(--motion)) var(--ease-spring) both}
  .lights .bulb.justlit .halo{animation:lithalo calc(900ms * var(--motion)) var(--ease-decel) both}
}
@keyframes lit{0%{opacity:.3;transform:scale(.8)}60%{opacity:1;transform:scale(1.3)}100%{opacity:1;transform:scale(1)}}
@keyframes lithalo{0%{opacity:0;transform:scale(.4)}50%{opacity:1;transform:scale(1.4)}100%{opacity:.9;transform:scale(1)}}

/* ── Wide layouts (≥1100 px) ─────────────────────────────────────────────── */
@media (min-width:1100px){
  .wide{width:100%;max-width:1240px;margin:0 auto;display:grid;column-gap:40px;row-gap:28px;align-items:start}
  .wide .mast{grid-column:1/-1;position:relative;isolation:isolate}
  .wide .mast::before{content:"";position:absolute;inset:0;border-radius:inherit;z-index:-1;background:var(--tod,none);
    transition:background calc(1200ms * var(--motion)) var(--ease-standard);
    /* fade the wash in from the left: cut off at full strength it drew a hard edge against the rail */
    -webkit-mask-image:linear-gradient(to right, transparent, #000 45%);mask-image:linear-gradient(to right, transparent, #000 45%)}
  .wide .mast .eyebrow{margin:0 0 8px;font:600 12px/1 var(--f-ui);letter-spacing:.08em;text-transform:uppercase}
  .wide .mast .pcap{margin-top:10px}
  .wide .mast .warnchip{display:inline-block;margin-top:12px}
  .wide .paneh{margin:0;font:600 11px/1 var(--f-ui);letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}
  .wide .panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);padding:20px 22px}
  .wide .panehead{display:flex;justify-content:space-between;align-items:baseline;gap:12px}

  /* Lok — broadsheet */
  .broadsheet{grid-template-columns:minmax(0,1fr) 320px}
  .broadsheet .mast{display:grid;grid-template-columns:minmax(0,1fr) minmax(300px,440px);gap:48px;align-items:end;
    padding:22px 26px 20px;margin:0 -26px;border-radius:var(--r-lg)}
  .broadsheet .masthello{container-type:inline-size;min-width:0}
  .broadsheet .inkh{font:600 min(60px,10.4cqi)/1.02 Newsreader,serif;letter-spacing:-.015em;white-space:nowrap}   /* one line that always fits its column */
  .wcol{display:grid;gap:28px;align-content:start;min-width:0}
  .broadsheet .next{border-top:3px solid var(--accent)}
  .broadsheet .next .title{font-size:32px}
  .broadsheet .count .big{font-size:96px;line-height:.85;letter-spacing:-.03em}
  .broadsheet .upgrp{margin:16px 0 2px;font:600 12px var(--f-ui);color:var(--ink)}
  .broadsheet .upgrp:first-of-type{margin-top:4px}
  .workpanel .stripbody,.workpanel .barscroll{padding:0}

  /* Time-of-day tints (Lok light / dark) */
  .app [data-tod=dawn]{--tod:linear-gradient(100deg,oklch(.95 .03 30 / .75),oklch(.96 .02 250 / .45))}
  .app [data-tod=morning]{--tod:linear-gradient(100deg,oklch(.955 .028 230 / .8),oklch(.97 .015 100 / .5))}
  .app [data-tod=afternoon]{--tod:linear-gradient(100deg,oklch(.95 .04 80 / .75),oklch(.965 .025 60 / .45))}
  .app [data-tod=evening]{--tod:linear-gradient(100deg,oklch(.91 .04 280 / .65),oklch(.94 .04 40 / .45))}
  .app [data-tod=night]{--tod:linear-gradient(100deg,oklch(.87 .05 265 / .6),oklch(.92 .03 265 / .4))}
  .app.dark [data-tod=dawn]{--tod:linear-gradient(100deg,oklch(.28 .05 20 / .6),oklch(.24 .03 260 / .45))}
  .app.dark [data-tod=morning]{--tod:linear-gradient(100deg,oklch(.27 .05 240 / .7),oklch(.24 .03 240 / .4))}
  .app.dark [data-tod=afternoon]{--tod:linear-gradient(100deg,oklch(.27 .04 70 / .55),oklch(.24 .03 60 / .4))}
  .app.dark [data-tod=evening]{--tod:linear-gradient(100deg,oklch(.25 .06 300 / .65),oklch(.23 .04 30 / .45))}
  .app.dark [data-tod=night]{--tod:linear-gradient(100deg,oklch(.15 .03 265 / .85),oklch(.17 .03 265 / .6))}
  .broadsheet .mast{border:1px solid var(--line-soft)}

  /* Semester river */
  .river{display:grid;gap:8px;min-width:0}
  .rtrack{position:relative;display:grid;grid-template-columns:repeat(var(--n),1fr);align-items:end;min-height:84px}
  .rtrack::before{content:"";position:absolute;left:0;right:0;bottom:17px;height:1px;background:var(--line)}
  .rwk{position:relative;align-self:stretch;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:3px;
    border:0;background:none;padding:0;cursor:pointer;color:var(--muted);font:500 10px var(--f-ui);border-radius:var(--r-sm)}
  .rwk:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  .rpins{display:flex;flex-direction:column-reverse;gap:3px;align-items:center;margin-bottom:6px}
  .rpin{display:inline-block;width:9px;height:9px;border-radius:50%;background:oklch(var(--c-dot) .13 var(--h,265))}
  .rpin.done{background:none;box-shadow:inset 0 0 0 1.5px oklch(var(--c-dot) .13 var(--h,265));opacity:.6}
  .rtick{width:1px;height:6px;background:var(--line)}
  .rwk.past{opacity:.45}
  .rwk.now{color:var(--accent);font-weight:700}
  .rwk.now .rtick{width:9px;height:9px;border-radius:50%;background:var(--accent);margin-bottom:-2px;box-shadow:0 0 0 4px var(--accent-soft)}
  .rwk.hol::after,.rhatch{content:"";background:repeating-linear-gradient(135deg,var(--warn-ink) 0 1.5px,transparent 1.5px 4px);opacity:.55;border-radius:2px}
  .rwk.hol::after{position:absolute;bottom:13px;left:15%;right:15%;height:9px}
  .rwk:hover .rtick{background:var(--ink)}
  .rwk:hover .rnum{color:var(--ink)}
  .rlegend{display:flex;gap:14px;justify-content:flex-end;margin:0;font:500 11px var(--f-ui);color:var(--muted)}
  .rlegend span{display:inline-flex;gap:6px;align-items:center}
  .rlegend .rpin{width:8px;height:8px}
  .rhatch{display:inline-block;width:14px;height:8px}

  /* Live day column */
  .daycol .dtrack{position:relative;margin:18px 0 6px;height:calc(var(--hours) * 46px)}
  .dhr{position:absolute;left:0;right:0;height:12px;transform:translateY(-50%);font:500 10px/12px "DM Mono",monospace;color:var(--muted)}
  .dhr::after{content:"";position:absolute;left:42px;right:0;top:50%;border-top:1px dashed var(--line-soft)}
  .dgap,.dblk{position:absolute;left:48px;right:0;border-radius:8px}
  .dgap{display:flex;align-items:center;justify-content:center;font:500 11px var(--f-ui);color:var(--ok);
    border:1px dashed color-mix(in oklch,var(--ok) 40%,transparent);margin:2px 0;box-sizing:border-box}
  .dblk{padding:5px 10px;font-size:12px;line-height:1.25;overflow:hidden;
    background:oklch(var(--c-l) var(--c-c) var(--h));color:oklch(var(--c-ink) .1 var(--h));border-left:3px solid oklch(var(--c-dot) .14 var(--h))}
  .dblk b{display:block;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .dblk span{display:block;opacity:.85}
  .dblk.past,.dgap.past{opacity:.55}
  .dgap.past{border-color:var(--line-soft);color:var(--muted)}
  .dblk.ev{background:var(--accent);color:var(--surface);border-left-color:var(--ink)}
  .dnow{position:absolute;left:40px;right:-6px;height:0;border-top:2px solid var(--urgent);z-index:2}
  .dnow::before{content:"";position:absolute;left:-5px;top:-6px;width:10px;height:10px;border-radius:50%;background:var(--urgent)}
  .dnow span{position:absolute;right:0;top:-17px;font:600 10px "DM Mono",monospace;color:var(--urgent);background:var(--surface);padding:0 3px}
  .dayfree{margin:14px 0 4px;font:var(--title);font-size:21px}
  .daynote{display:inline-block;margin:12px 0 0;padding:3px 9px;border-radius:999px;background:var(--warn-bg);color:var(--warn-ink);font:600 12px var(--f-ui)}
  .daynext{margin:0;color:var(--muted);font-size:13px}
  @media (prefers-reduced-motion:no-preference){ .dnow{transition:top 1s var(--ease-standard)} }

  /* Course spines */
  .spines{display:flex;gap:12px;align-items:flex-end;height:190px;margin-top:16px;padding:0 4px}
  .spine{position:relative;flex:1;height:100%;border:0;padding:0;cursor:pointer;overflow:hidden;
    background:color-mix(in oklch,oklch(var(--c-dot) .1 var(--h)) 14%,var(--surface));
    clip-path:polygon(0 0,100% 0,100% 100%,50% calc(100% - 14px),0 100%)}
  .spine:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
  .sfill{position:absolute;left:0;right:0;bottom:0;height:var(--f);background:oklch(var(--c-dot) .13 var(--h))}
  .slbl{position:absolute;inset:26px 0 22px;writing-mode:vertical-rl;transform:rotate(180deg);display:flex;align-items:center;justify-content:flex-end;
    font:600 13px var(--f-ui);letter-spacing:.04em;color:var(--ink)}
  .spc{position:absolute;top:8px;left:0;right:0;text-align:center;font:600 10px "DM Mono",monospace;color:var(--muted)}
  .spinecap{margin:10px 0 0;font:500 11px var(--f-ui);color:var(--muted)}
  @media (prefers-reduced-motion:no-preference){
    .spine{transition:transform var(--dur-fast) var(--ease-standard)}
    .spine:hover{transform:translateY(-4px)}
    .sfill{transition:height calc(900ms * var(--motion)) var(--ease-spring)}
  }

  /* Zi Xuan — quiet: Next up + Coming up, lots of air */
  .wide.quiet{grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);column-gap:56px;row-gap:34px}
  .quiet .mast{padding:6px 0 0}
  .quiet .mast::before{inset:-90px -40px -20px}
  .quiet .mast .h1{font:italic 600 64px/1 "Cormorant Garamond",serif}
  .quiet .next{padding:28px 30px;border-color:var(--line-soft);background:color-mix(in oklch,var(--surface) 82%,transparent)}
  .quiet .count .big{font-size:104px;line-height:.85}
  .quiet .upnext{margin-top:0;padding-top:4px}
  .app.xuan [data-tod=dawn]{--tod:radial-gradient(60% 120% at 20% 0%,oklch(.32 .06 40 / .45),transparent 70%)}
  .app.xuan [data-tod=morning]{--tod:radial-gradient(60% 120% at 20% 0%,oklch(.3 .05 60 / .3),transparent 70%)}
  .app.xuan [data-tod=afternoon]{--tod:radial-gradient(60% 120% at 20% 0%,oklch(.32 .06 55 / .35),transparent 70%)}
  .app.xuan [data-tod=evening]{--tod:radial-gradient(60% 120% at 20% 0%,oklch(.36 .09 50 / .5),transparent 70%)}
  .app.xuan [data-tod=night]{--tod:radial-gradient(60% 130% at 20% 0%,oklch(.4 .11 45 / .6),transparent 75%)}
}

/* Coming up, course + title only (Zi Xuan wide) */
.uprow.quiet{grid-template-columns:minmax(0,1fr);gap:3px;padding:14px 6px}
.uprow.quiet .uptitle{font:600 22px/1.15 "Cormorant Garamond",serif;white-space:normal}

@media (prefers-reduced-motion:no-preference){
  #view.entering :is(.mast,.panel,.wcol>.upnext){animation:rise var(--dur-slow) var(--ease-decel) both}
  #view.entering .wcol.wside>.panel:nth-child(2){animation-delay:calc(60ms * var(--motion))}
}
