/* Planner v3.1 — simplified Today/Tasks, progress line, editable times, morphing pills.
   Loaded last; overrides earlier rules on purpose (each override says what it replaces). */

/* ── Shared bits ─────────────────────────────────────────────────────────── */
button,input,select,textarea{font-family:inherit}   /* buttons default to the system font (rows rendered in Arial) */
.clabel{display:inline-flex;align-items:center;gap:7px;min-width:0;font:500 13px var(--f-ui);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cdot{width:8px;height:8px;border-radius:50%;flex:none;background:oklch(var(--c-dot) .12 var(--h))}
.app.xuan,.app.xuan *{font-variant-numeric:lining-nums !important}
/* ↑ Cormorant's default old-style figures make "1" read as "I" (A1 → AI) and "0" as "o".
   Set on every element (not inherited): any `font:` shorthand resets font-variant-numeric. */
.sech{margin:0 0 8px;font:600 12px var(--f-ui);letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
.savebtn{border:0;background:var(--ink);color:var(--bg);border-radius:999px;padding:8px 16px;font:600 13px var(--f-ui);cursor:pointer}
.iconbtn{flex:none;width:32px;height:32px;display:grid;place-items:center;border:0;border-radius:8px;background:none;color:var(--muted);cursor:pointer}
.iconbtn:hover,.iconbtn[aria-expanded="true"]{background:var(--accent-soft);color:var(--ink)}
:is(.dueedit,.slotedit) label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--muted)}
:is(.dueedit,.slotedit) :is(input,select){border:1px solid var(--line);border-radius:var(--r-sm);padding:7px 10px;font:14px var(--f-ui);background:var(--bg);color:var(--ink);min-height:36px}

/* ── Rail: one search entry instead of a search bar on every page ────────── */
.railsearch{display:flex;align-items:center;gap:12px;width:100%;height:40px;margin-top:10px;padding:0 10px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);color:var(--muted);font:500 15px var(--f-ui);cursor:pointer;text-align:left}
.railsearch .ico{width:20px;height:20px}
.railsearch:hover{color:var(--ink);border-color:var(--muted)}
.signin.restoring{height:34px;border-radius:999px;background:var(--line-soft);opacity:.7}
@media (max-width:719px){
  .railsearch{width:auto;height:auto;margin:0;padding:4px 8px;border:0;background:none;flex-direction:column;gap:2px;font-size:11px;flex:none}
}

/* ── Today: the focus column ─────────────────────────────────────────────── */
.focus{width:100%;max-width:720px;margin:0 auto;display:flex;flex-direction:column;gap:26px}
.ftop{display:grid;gap:10px}
.ftop .h1{margin:0}
.warnchip{justify-self:start;max-width:100%;background:var(--warn-bg);color:var(--warn-ink);border-radius:var(--r-md);font-size:13px}
.warnchip summary{list-style:none;display:flex;gap:8px;align-items:center;padding:7px 12px;cursor:pointer;font-weight:600}
.warnchip summary::-webkit-details-marker{display:none}
.warnchip ul{margin:0;padding:0 14px 10px 34px}
.next{margin:0}

/* Registered so the bar's colours and glow BLEND between tiers instead of snapping. */
@property --pl-a{syntax:'<color>';inherits:true;initial-value:transparent}
@property --pl-b{syntax:'<color>';inherits:true;initial-value:transparent}
@property --glow{syntax:'<length>';inherits:true;initial-value:0px}

/* Progress bar — one smooth, living bar. --p = share of tasks done; data-tier
   (0–4, domain/progress.js tierOf) sets its mood: colour warms from the theme
   accent to gold, the shimmer quickens, then glow, pulse and floating sparks. */
.pline{display:grid;gap:10px;
  --gold:oklch(.78 .15 78);--amber:oklch(.7 .16 52);
  --pl-a:var(--accent);--pl-b:color-mix(in oklch,var(--accent) 75%,white);--shim:3.4s;--glow:0px;
  --pl-ease:cubic-bezier(.22,1,.36,1);   /* long, gentle ease-out: no overshoot, no bounce */
  transition:--pl-a calc(1.6s * var(--motion)) ease,--pl-b calc(1.6s * var(--motion)) ease,--glow calc(1.6s * var(--motion)) ease}
.pline[data-tier="1"]{--pl-b:color-mix(in oklch,var(--accent) 55%,var(--amber));--shim:2.8s}
.pline[data-tier="2"]{--pl-a:color-mix(in oklch,var(--accent) 70%,var(--amber));--pl-b:var(--amber);--shim:2.2s;--glow:10px}
.pline[data-tier="3"]{--pl-a:var(--amber);--pl-b:var(--gold);--shim:1.7s;--glow:14px}
.pline[data-tier="4"]{--pl-a:var(--gold);--pl-b:oklch(.9 .12 95);--shim:2.6s;--glow:18px}
.plhead{display:flex;justify-content:space-between;align-items:baseline;gap:12px;flex-wrap:wrap}
.plcount{margin:0;font:var(--title);color:var(--ink)}
.plnote{font:600 14px var(--f-ui);color:color-mix(in oklch,var(--pl-a) 60%,var(--ink))}
.plgrade{margin:0;font:500 13px var(--f-ui);color:var(--muted)}
.pltrack{position:relative;height:12px;border-radius:999px;background:var(--line-soft);
  box-shadow:inset 0 1px 2px rgb(0 0 0/.08)}
.plfill{position:relative;height:100%;width:var(--p);border-radius:inherit;
  background:linear-gradient(90deg,var(--pl-a),var(--pl-b));
  box-shadow:0 0 var(--glow) calc(var(--glow) / -4) var(--pl-b);
  transition:width calc(1400ms * var(--motion)) var(--pl-ease)}
/* the shimmer lives on its own clipped layer, so it never restarts when the fill changes */
.plshine{position:absolute;inset:0;border-radius:inherit;overflow:hidden;pointer-events:none}
.plshine::before{content:"";position:absolute;top:0;bottom:0;left:0;width:60%;
  background:linear-gradient(100deg,transparent,rgb(255 255 255/.3) 50%,transparent);transform:translateX(-120%)}
.pline.is-empty .plshine{display:none}
/* the glowing head at the leading edge (hidden while empty — no stray mark at 0) */
.plfill::after{content:"";position:absolute;right:-3px;top:50%;width:14px;height:14px;margin-top:-7px;border-radius:50%;
  background:radial-gradient(circle,rgb(255 255 255/.95),var(--pl-b) 45%,transparent 70%);opacity:.55}
.pline[data-tier="0"] .plfill::after{opacity:.35}
.pline.is-empty .plfill::after,.pline.compact .plfill::after{display:none}
.pline.compact{gap:0}
.pline.compact .pltrack{height:6px}
.pline.compact .plfill{box-shadow:none}
/* floating sparks (nearly there + complete) and the one-shot burst (ui/progressFx.js) */
.spark{position:absolute;right:0;top:50%;width:4px;height:4px;border-radius:50%;background:var(--gold);opacity:0;pointer-events:none}
.plspark{position:absolute;left:var(--p);top:50%;width:5px;height:5px;margin:-2.5px 0 0 -2.5px;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,white,var(--pl-b) 60%);z-index:3}

@media (prefers-reduced-motion:no-preference){
  #view.entering :is(.pline,.upnext,.strips,.ftop){animation:rise var(--dur-slow) var(--ease-decel) both}
  #view.entering .plfill{animation:plgrow calc(1600ms * var(--motion)) var(--pl-ease) both}
  .plshine::before{animation:plshimmer calc(var(--shim) * var(--motion)) cubic-bezier(.45,0,.55,1) infinite}
  .pline:not(.compact) .plfill::after{animation:plbreathe calc(3s * var(--motion)) ease-in-out infinite}
  .pline[data-tier="3"]:not(.compact) .pltrack,.pline[data-tier="4"]:not(.compact) .pltrack{animation:plpulse calc(2.2s * var(--motion)) ease-in-out infinite}
  .spark{animation:plfloat calc(1.8s * var(--motion)) ease-out infinite}
  .spark.s2{animation-delay:calc(.6s * var(--motion));right:4px}
  .spark.s3{animation-delay:calc(1.2s * var(--motion));right:-3px}
  .pline.bump .plfill::after{animation:plflare calc(900ms * var(--motion)) var(--pl-ease) 1}
  .pline.nudge .plfill::after{animation:plnudge calc(700ms * var(--motion)) var(--pl-ease) 1}
  .plspark{animation:plburst var(--sd,700ms) var(--pl-ease) forwards}
}
@keyframes plgrow{from{width:0}}
@keyframes plshimmer{0%{transform:translateX(-120%)}70%,100%{transform:translateX(260%)}}
@keyframes plbreathe{50%{opacity:.95;transform:scale(1.18)}}
@keyframes plnudge{0%{transform:scale(1)}35%{transform:scale(1.7);opacity:1}100%{transform:scale(1)}}
@keyframes plpulse{50%{box-shadow:inset 0 1px 2px rgb(0 0 0/.08),0 0 16px -4px var(--pl-b)}}
@keyframes plfloat{0%{opacity:0;transform:translate(0,0) scale(.6)}25%{opacity:1}100%{opacity:0;transform:translate(6px,-16px) scale(.2)}}
@keyframes plflare{0%{transform:scale(1);opacity:1}40%{transform:scale(2.6);opacity:1}100%{transform:scale(1);opacity:.55}}
@keyframes plburst{0%{opacity:1;transform:translate(0,0) scale(1)}100%{opacity:0;transform:translate(var(--dx),var(--dy)) scale(.3)}}

/* Coming up */
.upnext{position:relative}
.xuan .upnext{margin-top:34px}                       /* room for the skeleton to sit on the list, clear of Next up */
.xuan .upnext .perch{top:-44px}
.uplist{display:grid;border-top:1px solid var(--line)}
.uprow{display:grid;grid-template-columns:minmax(0,200px) minmax(0,1fr) auto 76px;gap:14px;align-items:center;width:100%;padding:12px 8px;border:0;border-bottom:1px solid var(--line-soft);background:none;color:var(--ink);font:14px var(--f-ui);text-align:left;cursor:pointer;border-radius:0}
.uprow:hover{background:var(--accent-soft)}
.uprow:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.uptitle{font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.updue{font:500 12px "DM Mono",monospace;color:var(--muted);white-space:nowrap}
.uprow .chip{justify-self:end}
.seeall{display:inline-flex;gap:6px;align-items:center;margin-top:12px;color:var(--accent);font:600 13px var(--f-ui);text-decoration:none}
.seeall:hover{text-decoration:underline}
@media (max-width:600px){
  .uprow{grid-template-columns:minmax(0,1fr) auto;gap:4px 10px}
  .uprow .clabel{grid-column:1/-1}
  .uprow .updue{display:none}
}

/* Folded strips */
.strips{display:grid;gap:8px}
.strip{border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface)}
.strip summary{list-style:none;display:flex;justify-content:space-between;align-items:center;gap:12px;padding:12px 16px;cursor:pointer;font:600 14px var(--f-ui);color:var(--ink)}
.strip summary::-webkit-details-marker{display:none}
.strip summary>span:first-child::before{content:"";display:inline-block;width:6px;height:6px;margin:0 10px 2px 0;border-right:1.5px solid var(--muted);border-bottom:1.5px solid var(--muted);transform:rotate(-45deg);transition:transform var(--dur-fast) var(--ease-standard)}
.strip[open] summary>span:first-child::before{transform:rotate(45deg)}
.stripn{color:var(--muted);font-weight:500}
.strippeek{font:500 12px var(--f-ui);color:var(--muted);text-align:right}
.stripbody{padding:2px 16px 16px}
.stripbody .clsrow{grid-template-columns:92px minmax(0,1fr) auto auto}

/* ── Tasks ───────────────────────────────────────────────────────────────── */
.backchip{align-self:flex-start;display:inline-flex;gap:6px;align-items:center;border:1px solid var(--line);background:var(--surface);border-radius:999px;padding:6px 12px;font:600 13px var(--f-ui);color:var(--ink);cursor:pointer}
.backchip:hover{border-color:var(--muted)}
.coursechips{display:flex;flex-wrap:wrap;gap:6px}
.chipfilter .cabbr{display:none}
@media (max-width:900px){.chipfilter .cfull{display:none}.chipfilter .cabbr{display:inline}}
/* replaces the 7-column row (badge, title, confidence, weight, urgency, status, chevron) */
.rowhead{grid-template-columns:10px minmax(0,1fr) auto 24px}
.rowhead .what small .edited{color:var(--accent)}
.rowmeta{display:flex;flex-wrap:wrap;gap:8px 14px;align-items:center;margin-top:14px;font-size:13px;color:var(--muted)}
.rowmeta .conf{display:inline-flex;gap:5px;align-items:center}
.rowmeta .conf .ico{width:15px;height:15px}
.rowmeta .note{flex-basis:100%}
@media (max-width:719px){
  .rowhead{grid-template-columns:10px minmax(0,1fr) 24px;gap:10px}
  .rowhead .status{grid-column:2;justify-self:start}
  .rowhead .chevron{position:static;grid-row:1;grid-column:3}
}
.coursegrp .cghead{display:flex;justify-content:space-between;align-items:baseline;gap:12px;flex-wrap:wrap;padding:8px 0 6px;border-bottom:1px solid var(--line);margin-bottom:6px}
.coursegrp .cghead h2{position:static;margin:0;padding:0;border:0;background:none;text-transform:none;letter-spacing:0}
.cghead .clabel{font:600 17px var(--f-ui);color:var(--ink)}
.xuan .cghead .clabel{font:var(--title);font-size:22px}
.cgstat{font:500 12px "DM Mono",monospace;color:var(--muted)}
.coursegrp .pline.compact{margin-bottom:10px}

/* Due-date editor (Tasks row + Calendar panel) */
.dueedit{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;padding:12px;border:1px dashed var(--line);border-radius:var(--r-md)}
.dueeditbtns{display:flex;gap:14px;align-items:center}

/* Segmented controls: one sliding thumb (ui/segs.js) instead of a background swap */
.seg{position:relative}
.seg>button{position:relative;z-index:1;transition:color var(--dur-fast) var(--ease-standard)}
.seg:has(>.segthumb)>button[aria-pressed="true"]{background:transparent;color:var(--bg)}
.segthumb{position:absolute;top:2px;bottom:2px;left:0;width:0;border-radius:999px;background:var(--ink);z-index:0;pointer-events:none;
  transition:transform var(--dur-base) var(--ease-standard),width var(--dur-base) var(--ease-standard),opacity var(--dur-fast)}

/* Status pill: label cross-fade + width ease come from ui/segs.js flipStatus; drop the old pop */
.status.just .ico,.status.just .lbl{animation:none}
.status{overflow:hidden;white-space:nowrap}

/* Course pills morph in place (ui/pillgrow.js) */
.course .ctext{position:relative;display:inline-block;overflow:hidden;white-space:nowrap;vertical-align:bottom}
.course .cs{display:inline-block}
.course .cf{position:absolute;left:0;top:0;opacity:0;white-space:nowrap}
.course.growing{position:relative;z-index:6;box-shadow:0 6px 16px -8px rgb(0 0 0/.35)}
.course.grown .cs{opacity:0}.course.grown .cf{opacity:1}
.course.full .ctext{text-overflow:ellipsis;max-width:100%}

/* ── Calendar ────────────────────────────────────────────────────────────── */
.calpanel .clsedit{width:100%;border:0;border-bottom:1px solid var(--line-soft);background:none;color:var(--ink);font:13px var(--f-ui);text-align:left;cursor:pointer;border-radius:6px}
.calpanel .clsedit:hover{background:var(--accent-soft)}
.panelline{display:flex;align-items:center;gap:6px}
.panelline .panelitem{flex:1;min-width:0;display:grid;gap:2px}
.pititle{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.panelsec .dueedit{margin:4px 0 10px}

/* Timetable: hour hairlines across the columns, half-hour faint lines, labels on the line */
.ttgrid{--hr:64px;grid-template-columns:56px repeat(var(--days,5),minmax(120px,1fr))}
.tthours{display:block;position:relative;height:calc(var(--hr) * 11)}
.tthour{position:absolute;right:10px;padding:0;transform:translateY(-50%);font:500 11px "DM Mono",monospace;font-variant-numeric:tabular-nums;color:var(--muted)}
.ttcol{height:calc(var(--hr) * 11);
  background-image:linear-gradient(to bottom,var(--line) 1px,transparent 1px),
    linear-gradient(to bottom,transparent calc(var(--hr) / 2),color-mix(in oklch,var(--line-soft) 70%,transparent) calc(var(--hr) / 2),color-mix(in oklch,var(--line-soft) 70%,transparent) calc(var(--hr) / 2 + 1px),transparent calc(var(--hr) / 2 + 1px));
  background-size:100% var(--hr)}
.ttday{font:600 13px var(--f-ui);color:var(--muted)}
.ttday.today{color:var(--accent)}
.ttday.today::after{content:"";display:block;width:18px;height:2px;margin:4px auto 0;border-radius:2px;background:var(--accent)}
.ttblock{border:0;text-align:left;cursor:pointer;display:flex;flex-direction:column;gap:1px;font:11px var(--f-ui);
  transition:box-shadow var(--dur-fast) var(--ease-standard),transform var(--dur-instant) var(--ease-standard)}
.ttblock.tutorial{border:1.5px dashed oklch(var(--c-ink) var(--c-c) var(--h))}
.ttblock:hover{box-shadow:0 6px 16px -10px rgb(0 0 0/.5)}
.ttblock:focus-visible,.ttblock.editing{outline:2px solid var(--accent);outline-offset:1px}
.ttblock .tttime{font:500 10.5px "DM Mono",monospace;text-transform:none;letter-spacing:0;opacity:.85}
.ttblock b{font-size:12px;line-height:1.2;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.ttblock small{text-transform:capitalize;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ttblock.short b{-webkit-line-clamp:1}
.ttscroll{overflow-y:hidden;padding-bottom:10px}   /* the 19:00 label sits on the last line, half below it */
.ttnow span{position:absolute;right:calc(100% + 2px);top:50%;transform:translateY(-50%);padding:0 4px;border-radius:4px;background:var(--bg);font:600 10px "DM Mono",monospace;color:var(--urgent)}
.tthint{margin:10px 0 0;font-size:12px;color:var(--muted)}
.noslot{display:flex;flex-wrap:wrap;gap:14px;margin-top:6px}

.slotedit{display:grid;gap:10px;margin:0 0 14px;padding:16px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface)}
.calpanel .slotedit{margin:10px 0 0;padding:12px;border-style:dashed}
.slothead{margin:0;display:flex;gap:10px;align-items:center}
.slotfields{display:flex;flex-wrap:wrap;gap:10px}
.slotfields input[name="room"]{width:110px}
.slotnote{margin:0;font-size:12px;color:var(--muted)}
.slotbtns{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.slotbtns>button:not(.savebtn):not(.linkbtn){border:1px solid var(--line);background:none;color:var(--ink);border-radius:999px;padding:7px 14px;font:600 13px var(--f-ui);cursor:pointer}

/* ── Settings: shortcuts listed once ─────────────────────────────────────── */
.shortcuts{display:grid;gap:8px;margin:0}
.shortcuts div{display:flex;justify-content:space-between;gap:12px;font-size:14px}
.shortcuts dt,.shortcuts dd{margin:0}
.shortcuts dd{color:var(--muted)}
.shortcuts kbd{font:500 12px "DM Mono",monospace;border:1px solid var(--line);border-radius:6px;padding:1px 6px}

/* Palette list bits (inline styles moved here) */
.palsub{color:var(--muted);font-size:12px}
.palnone{color:var(--muted)}

/* ── Motion: reduced-motion fallbacks (the no-preference rules sit with each component) ── */
@media (prefers-reduced-motion:reduce){
  .segthumb,.pline,.plfill,.strip summary>span:first-child::before{transition:none}
}

/* ── Zi Xuan's fairy lights (ui/decor.js lightsSVG — drawn at the real width) ── */
.lights-host{position:absolute;left:0;right:0;top:0;height:116px}
.lights{display:block;overflow:visible}
.lights .wire path{fill:none;stroke:oklch(.36 .035 330);stroke-width:1.15;stroke-linecap:round}
.lights .back .wire path{stroke:oklch(.3 .03 330);stroke-width:.9}
.lights .back{opacity:.55;filter:blur(.4px)}
.lights .drop{stroke:oklch(.36 .035 330);stroke-width:.9}
.lights .cap{fill:oklch(.52 .06 70)}
.lights .glint{fill:white;opacity:.75;transform-box:fill-box;transform-origin:center}
.lights .halo{mix-blend-mode:screen;transform-box:fill-box;transform-origin:center}
.xuan .rail{padding-top:74px}                 /* the garland runs over the rail now; keep the logo clear */
.xuan .main{padding-top:88px}                 /* deeper swags hang to ~y70 */
/* The garland hangs over one continuous sky: the rail's panel and its edge line fade in
   below the swags instead of boxing in the left end of the strand. */
@media (min-width:720px){
  .xuan .rail{background:linear-gradient(to bottom, transparent 0 64px, var(--rail) 140px);   /* transparent, not --bg: the page grain (.app::before) must show through */
    border-right:1px solid transparent;border-image:linear-gradient(to bottom, transparent 0 64px, var(--line) 140px) 1}
}
@media (max-width:719px){
  .lights-host{top:49px;height:84px}          /* hang below the fixed top bar (semester + people) */
  .xuan .main{padding-top:118px}
}
@media (prefers-reduced-motion:no-preference){
  .lights .bulb{animation:lsway calc(var(--sw) * var(--motion)) ease-in-out var(--sd) infinite alternate}
  .lights .glass{animation:ltwinkle calc(var(--tw) * var(--motion)) ease-in-out var(--td) infinite alternate}
  .lights .halo{animation:lhalo calc(var(--tw) * var(--motion)) ease-in-out var(--td) infinite alternate}
  .lights .sparkle .glint{animation:lsparkle calc(var(--sp) * var(--motion)) ease-in-out var(--spd) infinite}
}
@keyframes lsway{from{transform:rotate(-2.5deg)}to{transform:rotate(2.5deg)}}
@keyframes ltwinkle{from{opacity:1}to{opacity:.68}}
@keyframes lhalo{from{opacity:.95;transform:scale(1.05)}to{opacity:.45;transform:scale(.82)}}
@keyframes lsparkle{0%,88%,100%{opacity:.75;transform:none}93%{opacity:1;transform:scale(2.4)}}
