/* Kounsly — components added on top of the page's own design tokens.
   Everything here reuses --accent/--panel/--brd/--grad etc. so light and dark
   themes keep working without a second palette. */

/* ---------- result cards ----------
   The card's own look (typography, .facts grid, the green .free figure, the
   mono deadline) lives in index.html and is left alone on purpose — only the
   affordances for "this whole card opens" are added here. */
.card{cursor:pointer}
.card:hover{border-color:var(--brd-2)}
.card:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.card .lnk{pointer-events:none}   /* the whole card is the click target */

/* ---------- detail modal ---------- */
.sheet-wrap{
  position:fixed;inset:0;z-index:60;display:none;
  padding:5vh 20px;overflow:auto;
  background:rgba(10,11,17,.55);backdrop-filter:blur(3px);
}
.sheet-wrap.open{display:flex;align-items:flex-start;justify-content:center}
.sheet{
  position:relative;width:min(760px,100%);max-height:90vh;
  display:flex;flex-direction:column;
  background:var(--bg);border:1px solid var(--brd);border-radius:20px;
  box-shadow:0 24px 70px rgba(10,11,17,.35);
  overflow:hidden;
  animation:popin .2s cubic-bezier(.2,.7,.3,1);
}
@keyframes popin{from{transform:translateY(10px) scale(.985);opacity:0}to{transform:none;opacity:1}}
@media (prefers-reduced-motion:reduce){.sheet{animation:none}}
.sheet-head{
  flex:0 0 auto;padding:24px 28px 16px;
  background:var(--bg);border-bottom:1px solid var(--brd);
}
.sheet-close{
  position:absolute;top:16px;right:20px;width:34px;height:34px;border-radius:10px;
  border:1px solid var(--brd);background:var(--panel);color:var(--muted);
  font-size:1.1rem;line-height:1;cursor:pointer;
}
.sheet-close:hover{color:var(--text);border-color:var(--brd-2)}
.sheet-title{font-family:var(--serif);font-size:1.7rem;line-height:1.2;margin:0 8px 6px 0;padding-right:40px}
.sheet-sub{color:var(--muted);font-size:.95rem}
.sheet-body{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;padding:22px 28px 0}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:14px;margin:0 0 26px}
.tile{background:var(--panel);border:1px solid var(--brd);border-radius:13px;padding:13px 15px}
.tile b{display:block;color:var(--faint);font-size:.7rem;text-transform:uppercase;
  letter-spacing:.06em;font-weight:600;margin-bottom:5px}
.tile span{font-size:1.02rem;font-weight:600}
.tile.free span{color:var(--good)}
.sec{margin:0 0 24px}
.sec h3{font-size:.76rem;text-transform:uppercase;letter-spacing:.08em;color:var(--faint);
  margin:0 0 8px;font-weight:700}
.sec p, .sec .txt{margin:0;font-size:.95rem;line-height:1.62;white-space:pre-line;color:var(--text)}
.deadline-row{display:flex;gap:10px;align-items:baseline;padding:7px 0;border-bottom:1px dashed var(--brd)}
.deadline-row:last-child{border-bottom:0}
.deadline-row .term{font-size:.78rem;color:var(--faint);text-transform:uppercase;
  letter-spacing:.05em;min-width:74px}
.deadline-row .date{font-weight:600}
.deadline-row .left{margin-left:auto;font-size:.8rem;color:var(--muted)}
/* Labelled links: what you can do, and where it takes you. */
.linklist{display:flex;flex-direction:column;gap:8px;margin-top:10px}
.linkrow{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:12px 15px;border-radius:12px;border:1px solid var(--brd);
  background:var(--panel);text-decoration:none;color:var(--text);
  transition:border-color .16s ease, transform .16s ease;
}
.linkrow:hover{border-color:var(--accent);transform:translateX(2px)}
.linkrow:first-child{background:var(--accent-soft);border-color:transparent}
.lr-what{font-size:.93rem;font-weight:650;display:flex;flex-direction:column;gap:2px}
.lr-note{font-size:.78rem;font-weight:400;color:var(--muted)}
.lr-host{font-family:var(--mono);font-size:.78rem;color:var(--muted);white-space:nowrap}
.stamp{margin-top:22px;font-size:.78rem;color:var(--faint)}

/* ---------- selection → ask ---------- */
.selpop{
  position:absolute;z-index:80;display:none;transform:translate(-50%,-100%);
  padding:8px 13px;border-radius:10px;border:1px solid var(--brd-2);
  background:var(--panel);box-shadow:var(--shadow);
  font-size:.84rem;font-weight:600;color:var(--accent);cursor:pointer;white-space:nowrap;
}
.selpop.show{display:block}
.sheet-body ::selection{background:var(--accent-soft);color:var(--text)}

/* ---------- assistant ----------
   The conversation scrolls with the page; only the box you type into is parked
   at the bottom. This used to be one sticky element with the thread inside it,
   and two answers in it was taller than the sheet — a bottom-stuck box taller
   than its scrollport is held by its bottom edge and painted over everything
   above it, so the detail sheet looked frozen and the top of the conversation
   could not be reached at all. See askHTML in app.js. */
.ask{margin:24px 0 0;padding-top:18px;border-top:1px solid var(--brd)}
.ask-bar{
  position:sticky;bottom:0;margin:0 -28px;padding:12px 28px 20px;
  /* --bg, not --panel: in dark theme --panel is translucent white, and content
     scrolling underneath a sticky bar showed straight through it. */
  background:var(--bg);
  /* A shadow rather than a second rule: the section above already has a border,
     and this only has to say "the conversation passes underneath me". */
  box-shadow:0 -10px 14px -10px rgba(10,11,17,.28);
}
.ask-quote{
  display:none;margin-bottom:10px;padding:9px 12px;border-left:3px solid var(--accent);
  background:var(--accent-soft);border-radius:0 8px 8px 0;font-size:.85rem;color:var(--muted);
}
.ask-quote.show{display:block}
.ask-quote button{float:right;border:0;background:none;color:var(--muted);cursor:pointer;font-size:1rem;line-height:1}
.ask-form{display:flex;gap:9px}
.ask-form input{
  flex:1;padding:12px 14px;border-radius:11px;border:1px solid var(--brd-2);
  background:var(--panel);color:var(--text);font:inherit;font-size:.93rem;
}
.ask-form input:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:transparent}
.ask-form button{
  padding:12px 18px;border-radius:11px;border:0;background:var(--grad);color:#fff;
  font:inherit;font-weight:600;cursor:pointer;
}
.ask-form button:disabled{opacity:.5;cursor:default}
.ask-hint{margin-top:8px;font-size:.75rem;color:var(--faint)}
.thread{display:flex;flex-direction:column;gap:12px}
.thread:not(:empty){margin:0 0 14px}
.msg{font-size:.93rem;line-height:1.6}
.msg.me{align-self:flex-end;max-width:85%;background:var(--accent-soft);color:var(--text);
  padding:10px 13px;border-radius:13px 13px 3px 13px}
/* anywhere, not break-word: answers quote bare URLs from the source pages, and
   one long one used to push the sheet sideways. */
.msg.ai{max-width:95%;white-space:pre-line;overflow-wrap:anywhere}
.msg.ai a{color:var(--accent)}
.msg.ai ul{white-space:normal;margin:6px 0;padding-left:20px}
.msg.ai li{margin:3px 0}
.msg.ai b{font-weight:650}
.msg.err{color:var(--warn)}
/* What the full-screen wall leaves behind when it is closed. Styled as an
   invitation rather than an error: nothing went wrong, the free part ended. */
.msg.wall{
  white-space:normal;max-width:100%;background:var(--accent-soft);border-radius:13px;
  padding:14px 15px;display:flex;flex-direction:column;align-items:flex-start;gap:11px;
}
.msg.wall .wall-t{color:var(--text)}
.msg.wall .wall-btn{
  padding:10px 16px;border-radius:11px;border:0;background:var(--grad);color:#fff;
  font:inherit;font-size:.88rem;font-weight:600;cursor:pointer;
}
.dots::after{content:"·";animation:dots 1.1s infinite steps(4);}
@keyframes dots{0%{content:"·"}33%{content:"··"}66%{content:"···"}}

/* ---------- misc ---------- */
.pager{display:flex;gap:10px;justify-content:center;align-items:center;margin:22px 0 8px}
.pager button{padding:9px 15px;border-radius:10px;border:1px solid var(--brd);
  background:var(--panel);color:var(--text);font:inherit;cursor:pointer}
.pager button:disabled{opacity:.4;cursor:default}
.pager span{color:var(--muted);font-size:.88rem}
.broadened{font-size:.85rem;color:var(--warn);margin-bottom:10px}

/* How the phrase was understood — visible, and dismissible. */
.read{display:flex;flex-wrap:wrap;align-items:center;gap:7px;margin:0 0 14px}
.read-k{font-size:.78rem;text-transform:uppercase;letter-spacing:.06em;color:var(--faint);font-weight:600}
.read-chip{
  font-size:.82rem;font-weight:600;padding:4px 10px;border-radius:999px;
  background:var(--accent-soft);color:var(--accent-ink);
}
.read-off{
  margin-left:2px;border:0;background:none;color:var(--muted);
  font:inherit;font-size:.8rem;text-decoration:underline;cursor:pointer;
}
.read-off:hover{color:var(--text)}
.skeleton{height:118px;border-radius:16px;background:var(--panel-2);margin-bottom:12px;
  animation:pulse 1.2s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:.55}50%{opacity:.9}}

/* ---------- deadlines with a group ---------- */
.deadline-row .who{font-size:.78rem;color:var(--muted)}


/* ---------- how to apply ---------- */
.step{display:flex;gap:12px;padding:11px 0}
.step + .step{border-top:1px dashed var(--brd)}
.step-n{
  flex:0 0 24px;height:24px;border-radius:50%;background:var(--accent);color:#fff;
  font-size:.78rem;font-weight:700;display:flex;align-items:center;justify-content:center;
}
.step.missing .step-n{background:var(--brd-2);color:var(--muted)}
.step-b{min-width:0}
.step-k{font-size:.74rem;text-transform:uppercase;letter-spacing:.05em;color:var(--faint);font-weight:600}
.step-v{font-size:.94rem;line-height:1.55;margin-top:2px;overflow-wrap:anywhere}
.step-v a{color:var(--accent)}
.step.missing .step-v{color:var(--muted);font-style:italic}
.step-note{font-size:.78rem;color:var(--muted);margin-top:3px}

/* ---------- template rows ---------- */
.grp{margin:0 0 26px}
.grp h3{
  font-size:.74rem;text-transform:uppercase;letter-spacing:.09em;color:var(--accent-ink);
  font-weight:700;margin:0 0 10px;padding-bottom:7px;border-bottom:1px solid var(--brd);
}
.row{display:grid;grid-template-columns:170px 1fr;gap:14px;padding:9px 0}
.row + .row{border-top:1px dashed var(--brd)}
.row-k{font-size:.82rem;color:var(--faint);font-weight:600;line-height:1.5}
.row-v{font-size:.94rem;line-height:1.62;white-space:pre-line;min-width:0;overflow-wrap:anywhere}
.row.missing .row-v{color:var(--muted);font-style:italic;white-space:normal}
.row.missing a{color:var(--accent);font-style:normal;white-space:nowrap}
.row-v .deadline-row:first-child{padding-top:0}

/* ---------- mobile ---------- */
@media (max-width:700px){
  .sheet-wrap{padding:0;overflow:hidden}
  .sheet-wrap.open{align-items:stretch}
  .sheet{
    width:100%;max-width:none;max-height:100dvh;height:100dvh;
    border-radius:0;border:0;animation:none;
  }
  .sheet-head{padding:16px 16px 12px}
  .sheet-title{font-size:1.25rem;padding-right:44px}
  .sheet-sub{font-size:.85rem}
  .sheet-close{top:12px;right:12px}
  .sheet-body{padding:16px 16px 0}
  .ask{margin:20px 0 0;padding-top:14px}
  .ask-bar{margin:0 -16px;padding:10px 16px calc(12px + env(safe-area-inset-bottom))}
  .ask-form input{font-size:1rem}          /* 16px stops iOS zooming the page in */
  .grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .tile{padding:11px 12px}
  .tile span{font-size:.95rem}
  /* Label above value: a 170px label column leaves nothing for the text. */
  .row{grid-template-columns:1fr;gap:3px;padding:11px 0}
  .row-k{font-size:.75rem;text-transform:uppercase;letter-spacing:.05em}
  /* Long portal hostnames were running off the screen. */
  .linkrow{flex-direction:column;align-items:flex-start;gap:6px}
  .lr-host{font-size:.72rem;overflow-wrap:anywhere;white-space:normal}
  .deadline-row{flex-wrap:wrap}
  .deadline-row .left{margin-left:0;width:100%}
}

.row.wide{grid-template-columns:1fr}

/* Safety net for source fields that arrive far longer than their slot: the level
   badge is built for "MSc", and a sentence in that field used to widen the whole
   page on a phone. */
.lvl{max-width:44%;white-space:normal;text-align:right;overflow-wrap:anywhere}

/* Facts look the same inside the modal as on the card that opened it.
   The card renders values in --mono with tabular numerals (.fact .v in
   index.html); the modal used the body sans, so opening a programme visibly
   changed the typeface of the very same figure. Prose stays sans. */
.tile span{font-family:var(--mono);font-variant-numeric:tabular-nums}
.deadline-row .date{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:.95rem}
.step-v b{font-family:var(--mono);font-variant-numeric:tabular-nums}
.row.money-row .row-v{font-family:var(--mono);font-variant-numeric:tabular-nums}

/* The modal heading matches the list. --serif here was a trap: the page's
   Playfair @font-face covers only the Cyrillic unicode-range, so a Ukrainian
   heading rendered in Playfair while an English programme name fell back to
   Georgia — two different typefaces for the same element, neither matching the
   card the reader had just clicked. */
.sheet-title{font-family:var(--sans);font-size:1.5rem;font-weight:700;letter-spacing:-.01em}

/* Mono suits figures, not sentences: "Без плати за навчання" set in monospace
   was the "wrong font" in the tiles. Values with no digits stay in the body face. */
.tile span.words{font-family:var(--sans);font-variant-numeric:normal;font-weight:600}
.row.money-row .row-v{font-family:var(--sans)}

/* ---------- accounts ----------
   The sign-in dialog is deliberately quieter than the detail sheet: it is an
   interruption, and an interruption that shouts reads as a toll gate. Same
   tokens, smaller box, no gradient except on the one button that commits. */

.gate-wrap{
  position:fixed;inset:0;z-index:90;display:none;
  padding:6vh 20px;overflow:auto;
  background:rgba(10,11,17,.6);backdrop-filter:blur(4px);
}
.gate-wrap.open{display:flex;align-items:flex-start;justify-content:center}
.gate{
  position:relative;width:min(420px,100%);
  background:var(--bg);border:1px solid var(--brd);border-radius:20px;
  box-shadow:0 24px 70px rgba(10,11,17,.4);padding:30px 28px 22px;
  animation:popin .2s cubic-bezier(.2,.7,.3,1);
}
@media (prefers-reduced-motion:reduce){.gate{animation:none}}
.gate.onboard{width:min(500px,100%)}
.gate h2{font-family:var(--sans);font-size:1.28rem;font-weight:700;letter-spacing:-.01em;
  line-height:1.25;margin:0 34px 8px 0}
.gate-lede{margin:0 0 20px;font-size:.92rem;line-height:1.55;color:var(--muted)}
.gate-close{
  position:absolute;top:16px;right:16px;width:32px;height:32px;border-radius:10px;
  border:1px solid var(--brd);background:var(--panel);color:var(--muted);
  font-size:1rem;line-height:1;cursor:pointer;
}
.gate-close:hover{color:var(--text);border-color:var(--brd-2)}

.gate-choices{display:flex;flex-direction:column;gap:9px}
.gate-btn{
  display:flex;align-items:center;gap:11px;width:100%;
  padding:13px 16px;border-radius:12px;border:1px solid var(--brd-2);
  background:var(--panel);color:var(--text);font:inherit;font-size:.93rem;font-weight:600;
  cursor:pointer;transition:border-color .16s ease,background .16s ease;
}
.gate-btn:hover{border-color:var(--accent)}
.gate-btn .gi{display:flex;width:18px;justify-content:center;flex:0 0 18px}
.gate-btn.g-email .gi{color:var(--muted);font-size:1.05rem}

.gate-form{display:flex;gap:9px;margin-top:4px}
.gate-form input{
  flex:1;min-width:0;padding:12px 14px;border-radius:11px;border:1px solid var(--brd-2);
  background:var(--panel);color:var(--text);font:inherit;font-size:.93rem;
}
.gate-form input:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:transparent}
.gate-form button{
  padding:12px 17px;border-radius:11px;border:0;background:var(--grad);color:#fff;
  font:inherit;font-weight:600;cursor:pointer;white-space:nowrap;
}
.gate-form button:disabled{opacity:.5;cursor:default}
/* Address above password above button: side by side, the two fields read as
   one thing to fill in and the submit ends up too narrow to name what it does. */
.gate-form.stack{flex-direction:column}
.gate-form.stack button{width:100%}
/* A six-digit code is a figure, and figures are mono everywhere else here. */
#gate-code-in{font-family:var(--mono);font-size:1.15rem;letter-spacing:.24em;text-align:center}
.gate-sent{margin:0 0 10px;font-size:.88rem;color:var(--muted)}
.gate-sub{margin:0 0 8px;font-family:var(--sans);font-size:.98rem;font-weight:650}
/* Sign in / create / forgotten / by code — the alternatives, kept quiet. The
   one someone needs is obvious to them and invisible to everyone else. */
.gate-links{display:flex;flex-wrap:wrap;gap:4px 16px;margin-top:2px}
.gate-links button{
  border:0;background:none;padding:0;color:var(--muted);
  font:inherit;font-size:.83rem;text-decoration:underline;cursor:pointer;
}
.gate-links button:hover{color:var(--text)}
.gate-err{margin-top:9px;font-size:.84rem;color:var(--warn);min-height:1.1em}
/* Same box, and sometimes the news in it is good — "sent again" in the colour
   reserved for "that didn't work" reads as a failure. */
.gate-err.ok{color:var(--muted)}
/* What happens after the button is pressed, said before it is. */
.gate-note{margin:9px 0 0;font-size:.82rem;line-height:1.45;color:var(--muted)}

/* A round trip that came back with a failure instead of a token. Loud enough to
   read before the buttons underneath it, and carrying the provider's own words:
   "redirect_uri_mismatch" in front of the person configuring it is worth an
   hour of guessing at why the click did nothing. */
.gate-alert{
  margin:14px 0 4px;padding:11px 13px;border-radius:11px;
  border:1px solid color-mix(in srgb,var(--warn) 40%,transparent);
  background:color-mix(in srgb,var(--warn) 9%,transparent);
}
.gate-alert p{margin:0;font-size:.86rem;line-height:1.45;color:var(--text)}
.gate-alert-detail{margin-top:6px !important;color:var(--muted) !important;font-size:.78rem !important}
.gate-alert code{
  font-family:var(--mono);font-size:.76rem;
  /* The provider writes this, and some of them write one long unbroken token. */
  word-break:break-word;
}
.gate-back{
  margin-top:6px;border:0;background:none;color:var(--muted);
  font:inherit;font-size:.83rem;text-decoration:underline;cursor:pointer;padding:0;
}
.gate-back:hover{color:var(--text)}
.gate-legal{margin:18px 0 0;font-size:.74rem;line-height:1.5;color:var(--faint)}

/* ---------- onboarding ---------- */
.ob-row{margin:0 0 16px}
.ob-row > b{display:block;font-size:.74rem;text-transform:uppercase;letter-spacing:.06em;
  color:var(--faint);font-weight:700;margin-bottom:7px}
.ob-opts{display:flex;flex-wrap:wrap;gap:7px}
.ob-opt{
  padding:8px 14px;border-radius:999px;border:1px solid var(--brd-2);
  background:var(--panel);color:var(--text);font:inherit;font-size:.86rem;font-weight:600;cursor:pointer;
}
.ob-opt:hover{border-color:var(--accent)}
.ob-opt.on{background:var(--accent-soft);border-color:transparent;color:var(--accent-ink)}
.ob-tests{display:flex;gap:9px}
.ob-tests input{
  width:110px;padding:10px 13px;border-radius:11px;border:1px solid var(--brd-2);
  background:var(--panel);color:var(--text);font:inherit;font-size:.9rem;
  font-family:var(--mono);font-variant-numeric:tabular-nums;
}
.ob-tests input:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:transparent}
/* Shown only when protected status is chosen: the one answer we refuse to act on. */
.ob-note{
  margin:0 0 16px;padding:11px 13px;border-left:3px solid var(--warn);
  background:var(--panel-2);border-radius:0 8px 8px 0;font-size:.82rem;line-height:1.5;color:var(--muted);
}
.ob-actions{display:flex;align-items:center;gap:12px;margin-top:20px}
.ob-actions .ob-save{
  margin-left:auto;padding:12px 24px;border-radius:11px;border:0;background:var(--grad);
  color:#fff;font:inherit;font-weight:600;cursor:pointer;
}
.ob-actions .ob-skip{
  border:0;background:none;color:var(--muted);font:inherit;font-size:.88rem;
  text-decoration:underline;cursor:pointer;padding:0;
}
.ob-actions .ob-skip:hover{color:var(--text)}

/* ---------- header ---------- */
#authslot{display:flex;align-items:center;gap:8px}
.signup{
  padding:8px 15px;border-radius:10px;border:0;background:var(--grad);color:#fff;
  font:inherit;font-size:.86rem;font-weight:700;cursor:pointer;white-space:nowrap;
}
.signup:hover{filter:brightness(1.06)}
.signin{
  padding:8px 15px;border-radius:10px;border:1px solid var(--brd-2);
  background:var(--panel);color:var(--text);font:inherit;font-size:.86rem;font-weight:600;cursor:pointer;
}
.signin:hover{border-color:var(--accent)}
/* One button, two labels — see paintHeader in auth.js. */
.lbl-narrow{display:none}

/* The header had no narrow case at all: brand, language, theme, "Увійти" and
   "Зареєструватися" at full size add up to 462px, and a phone is 390. The bar
   did not wrap or shrink, so the sign-up button — the one thing on the page
   that has to be pressable — was the part sitting past the right edge, and the
   whole document scrolled sideways. Trim the paddings, swap in the short
   label, and 462 becomes 348: it fits a 360px phone with room left. */
@media (max-width:560px){
  .top .wrap{padding:0 14px;gap:6px}
  .iconbtn{padding:6px 8px}
  #authslot{gap:6px}
  .signin,.signup{padding:8px 12px}
  .lbl-wide{display:none}
  .lbl-narrow{display:inline}
}
.acct{position:relative}
.acct-btn{width:34px;height:34px;border-radius:50%;border:0;background:var(--grad);
  color:#fff;font:inherit;font-weight:700;cursor:pointer;padding:0}
.acct-i{font-size:.9rem;line-height:1}
.acct-menu{
  position:absolute;right:0;top:calc(100% + 8px);z-index:70;min-width:200px;
  display:flex;flex-direction:column;padding:6px;
  background:var(--bg);border:1px solid var(--brd);border-radius:13px;box-shadow:var(--shadow);
}
.acct-menu[hidden]{display:none}
.acct-email{padding:8px 10px 9px;font-size:.78rem;color:var(--faint);
  border-bottom:1px solid var(--brd);margin-bottom:5px;overflow-wrap:anywhere}
.acct-menu button{
  text-align:left;padding:9px 10px;border-radius:9px;border:0;background:none;
  color:var(--text);font:inherit;font-size:.88rem;cursor:pointer;
}
.acct-menu button:hover{background:var(--panel-2)}

/* ---------- save / watch ----------
   The star sits on the card, not behind a menu: it is the first thing worth an
   account, so it has to be visible before anyone has one. */
/* The badge names the degree and the button keeps the programme; both want the
   same top-right corner, and the button was landing on top of "Master of
   Science". The badge gives way, because it is the one that can be read from
   the title anyway. */
.card:has(.save) .top{padding-right:46px}

/* Always there, in its own frame. It used to fade in on hover, which meant the
   one control worth an account was invisible until you happened to point at
   the card — and invisible altogether to anyone who never hovers. A button
   that keeps something has to look like a button before it is touched. */
.save{
  position:absolute;top:13px;right:14px;z-index:2;
  width:34px;height:34px;border-radius:10px;
  border:1px solid var(--brd-2);background:var(--panel);color:var(--faint);
  display:grid;place-items:center;cursor:pointer;line-height:0;
  transition:color .14s ease,border-color .14s ease,background .14s ease;
}
.card{position:relative}
.save:hover{color:var(--accent);border-color:var(--accent)}
.save.on{color:var(--accent);border-color:color-mix(in srgb,var(--accent) 40%,transparent);background:var(--accent-soft)}
.sheet-act svg{display:block}

/* In the detail sheet the two actions are stated in words — this is where the
   reader decides, and an unlabelled icon is not a decision. */
.sheet-acts{display:flex;flex-wrap:wrap;gap:9px;margin:0 0 22px}
.sheet-act{
  display:flex;align-items:center;gap:7px;padding:10px 15px;border-radius:11px;
  border:1px solid var(--brd-2);background:var(--panel);color:var(--text);
  font:inherit;font-size:.88rem;font-weight:600;cursor:pointer;
}
.sheet-act:hover{border-color:var(--accent)}
.sheet-act.on{background:var(--accent-soft);border-color:transparent;color:var(--accent-ink)}

/* The reader's own deadline, once the profile says which group they are in.
   The others stay fully visible: knowing your date is useful, being shown only
   your date is a claim we can't stand behind if the profile is wrong. */
.deadline-row.mine{background:var(--accent-soft);border-radius:9px;padding:7px 10px;border-bottom:0}
.deadline-row.mine .date{color:var(--accent-ink)}
.deadline-row.mine .who{color:var(--accent-ink);font-weight:600}

/* The remaining allowance, next to the hint that already explains the
   assistant. Shown before the wall, never only at it. */
.ask-left{color:var(--muted);font-weight:600}
.ask-left.low{color:var(--warn)}

/* ---------- locked filters ----------
   Drawn and greyed rather than hidden: a filter you can see and not use says
   what an account is for. The pointer-events line matters — a disabled select
   never receives the click, so the group around it is what opens the wall, and
   it can only do that if the control is not swallowing the event first. */
.fgroup.locked{opacity:.5;cursor:pointer}
.fgroup.locked select,
.fgroup.locked input,
.fgroup.locked .toggle{pointer-events:none}
.fgroup.locked h4::after{content:" 🔒";font-size:9px;letter-spacing:0;opacity:.8}

.flock{padding:14px 0;border-bottom:1px solid var(--brd)}
.flock .flock-t{margin:0 0 10px;font-size:12.5px;line-height:1.45;color:var(--muted)}
.flock .flock-btn{
  width:100%;padding:9px 12px;border-radius:10px;border:0;background:var(--grad);
  color:#fff;font:inherit;font-size:13px;font-weight:600;cursor:pointer;
}

/* Saved view: same list, different source. */
.savedbar{display:flex;align-items:center;gap:10px;margin:0 0 14px}
.savedbar h2{font-family:var(--sans);font-size:1.05rem;font-weight:700;margin:0}
.savedbar button{
  margin-left:auto;border:0;background:none;color:var(--muted);
  font:inherit;font-size:.84rem;text-decoration:underline;cursor:pointer;
}
.savedbar button:hover{color:var(--text)}

@media (max-width:700px){
  .gate-wrap{padding:0;align-items:stretch}
  .gate{width:100%;border-radius:0;border:0;min-height:100dvh;padding:26px 18px 24px;animation:none}
  .gate-form{flex-direction:column}
  .gate-form input{font-size:1rem}          /* 16px stops iOS zooming the page in */
  .gate-form button{width:100%}
  .ob-tests input{flex:1;width:auto}
}
