/* Clearance for whatever is stuck above the page.
   The logged-in admin bar (assets/adminbar.js) is `position:sticky; top:0` and
   is prepended to <body>, so it holds the top of the window on every page a
   staff account opens — including this product's own. It publishes its measured
   height into `--admin-bar-h` through the CSSOM (this product serves
   `style-src 'self'` with no `unsafe-inline`, so a `style=""` attribute would
   never apply, and the variable is how the number arrives). For a visitor it
   is never set and the fallback below is what everything reads.

   `--sticky-top` is what every surface of this product that pins itself to the
   top of the window uses for its own `top`, and what every `scroll-margin-top`
   adds: the workspace toolbar, the catalogue's table head, the step nav. They
   all said `top:0` before, which is the same line the admin bar occupies — so
   an admin saw the toolbar and the column heads slide underneath it and stay
   there. Nothing in this product pins itself to `0` any more. */
:root { --admin-bar-h:0px; --sticky-top:var(--admin-bar-h); --chip-gap:.3rem; --numeral-shift:.14em; --ink:#17233b; --muted:#5f6f89; --brand:#1b5e9c; --brand-strong:#154d80; --brand-dark:#173f5f; --brand-soft:#e9f0fb; --brand-tint:#dbe7f9; --brand-line:#c5d5ec; --mint:#e9f0fb; --paper:#f6f8fb; --surface:#fff; --line:#dfe5ee; --field-line:#bdcbe4; --field-line-hover:#91a9d6; --warm:#fff6e8; --info:#3277b3; --success:#25846f; --warning:#bd7418; --danger:#b5473c; --violet:#7256a8; --shadow-sm:0 1px 2px rgba(23,63,95,.05); --shadow-md:0 6px 16px rgba(23,63,95,.07); --shadow-pop:0 8px 24px rgba(23,63,95,.1); font-family:"Vazirmatn","Segoe UI",Tahoma,Arial,sans-serif; color:var(--ink); background:var(--paper); }
* { box-sizing:border-box; }
[hidden] { display:none !important; }
body { margin:0; min-height:100vh; background:var(--paper); line-height:1.8; }
a { color:inherit; }
.skip-link { position:absolute; inset-inline-start:.5rem; top:.5rem; z-index:10; transform:translateY(-180%); background:#fff; padding:.5rem 1rem; }
.skip-link:focus { transform:none; }
.site-header,.site-footer,main { width:var(--page-w); margin-inline:auto; }
.site-header { position:relative; min-height:var(--header-h); display:flex; align-items:center; justify-content:space-between; gap:1rem; border-bottom:1px solid var(--line); }.site-header .main-nav { display:flex; align-items:center; gap:1rem; }.site-header .main-nav>a:not(.button) { color:var(--brand-dark); font-weight:700; text-decoration:none; }
/* Colour and size for the whole footer; layout for it is declared once,
   with the rest of the footer's rules, further down. */
.site-footer { color:var(--muted); font-size:.9rem; }
.brand { display:inline-flex; align-items:center; gap:.65rem; text-decoration:none; font-weight:800; font-size:1.05rem; }.brand img { flex:none; }.brand span { color:var(--brand-dark); }
.button { display:inline-flex; align-items:center; justify-content:center; gap:var(--gap-glyph); min-height:var(--control-h); border:1px solid transparent; border-radius:11px; padding:.4rem 1.15rem; line-height:1.5; background:var(--brand); box-shadow:none; color:#fff; text-decoration:none; font:inherit; font-weight:700; cursor:pointer; }
.button:hover { background:var(--brand-dark); }.button.button-small { min-height:var(--action-h); padding:.3rem .8rem; line-height:1.45; font-size:.92rem; }.button-quiet { color:var(--brand-dark); background:var(--mint); }
.hero { display:grid; gap:2rem 3rem; padding:2.75rem 0 2.5rem; }
.card-kicker { color:var(--brand-dark); font-weight:800; letter-spacing:.01em; }
h1,h2,h3,p { margin-top:0; } h1 { max-width:760px; font-size:clamp(2.2rem,5vw,4.5rem); line-height:1.25; letter-spacing:-.04em; margin-bottom:1.5rem; }
.lead { max-width:680px; font-size:clamp(1rem,2vw,1.2rem); color:var(--muted); }.actions { display:flex; align-items:center; flex-wrap:wrap; gap:1.2rem; margin-top:2rem; }.text-link { color:var(--brand-dark); font-weight:700; }
.decision-card,.panel { border:1px solid var(--line); border-radius:24px; background:rgba(255,255,255,.86); box-shadow:0 24px 70px rgba(23,63,95,.1); padding:clamp(1.4rem,4vw,2.2rem); }
.decision-card ol { list-style:none; padding:0; margin:1.5rem 0 0; }.decision-card li { display:grid; grid-template-columns:4rem 1fr; gap:.7rem; align-items:center; padding:1.1rem 0; border-top:1px solid var(--line); }.decision-card strong { color:var(--brand-dark); }.decision-card span { color:var(--muted); }
.feature-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:1rem; padding:1rem 0 6rem; }.feature-grid>h2 { grid-column:1/-1; font-size:clamp(1.7rem,3vw,2.5rem); }.feature-grid article { background:var(--warm); border-radius:18px; padding:1.5rem; }.feature-grid article>span { color:var(--brand); font-weight:800; }.feature-grid article p { color:var(--muted); }
.workspace-head { display:flex; justify-content:space-between; gap:2rem; align-items:start; padding:4rem 0 2rem; }.workspace-head h1 { font-size:clamp(2rem,4vw,3.2rem); margin-bottom:.5rem; }.workspace-grid { display:grid; grid-template-columns:minmax(260px,.7fr) minmax(0,1.3fr); gap:1.5rem; padding-bottom:6rem; }.create-plan { align-self:start; }.panel label:not(.check-label):not(.selection-card) { display:block; font-weight:700; margin:.8rem 0 .35rem; }.panel input:not([type="checkbox"]):not([type="radio"]) { width:100%; border:1px solid #bdcbe4; border-radius:10px; padding:.78rem; font:inherit; margin-bottom:1rem; }.panel input:focus { outline:3px solid #cfe0f5; border-color:var(--brand); }.message-page { min-height:65vh; display:flex; flex-direction:column; align-items:start; justify-content:center; max-width:700px; }.message-page h1 { font-size:clamp(2rem,5vw,4rem); }
.how-flow-start fieldset { border:0; padding:0; margin:0 0 1rem; }.how-flow-start fieldset label:not(.check-label) { display:inline-flex; align-items:center; gap:.35rem; margin:0 .7rem .4rem 0; font-weight:400; }.how-flow-start fieldset input:not([type="checkbox"]):not([type="radio"]) { width:auto; margin:0; }.claim-draft { border-color:#b5c9f3; background:var(--mint); }
.auth-card { width:min(520px,100%); margin:5rem auto; }.auth-card h1 { font-size:2.2rem; }.form-error { border-radius:10px; background:#fff0ee; color:#8a241b; padding:.7rem 1rem; }
.success-message { border-radius:10px; background:var(--mint); color:var(--brand-dark); padding:.7rem 1rem; }.sr-only { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; }
.catalog-head { padding-bottom:1rem; }.catalog-filters { display:grid; grid-template-columns:auto minmax(150px,1fr) auto minmax(150px,1fr) auto minmax(150px,1fr) auto minmax(150px,1fr) auto; align-items:end; gap:.7rem; border-radius:18px; padding:1rem; }.catalog-filters label { margin:0; }.catalog-filters select,.catalog-filters input { min-height:48px; border:1px solid #bdcbe4; border-radius:10px; padding:.6rem; font:inherit; background:#fff; }.source-note { margin:1.5rem 0; color:var(--muted); }.help-text { color:var(--muted); }.status-pill { display:inline-flex; width:max-content; border-radius:999px; padding:.2rem .65rem; background:var(--mint); color:var(--brand-dark); font-size:.82rem; font-weight:800; }.status-unavailable { background:#fff0ee; color:#8a241b; }.status-unknown { background:var(--warm); color:#735c25; }.empty-state { margin:1rem 0 6rem; }.compact-panel { border-radius:14px; padding:1rem; box-shadow:none; }.compact-panel h3,.compact-panel p { margin-bottom:.2rem; }.compact-panel small { color:var(--muted); }
.decision-title h1 { margin-bottom:.4rem; }.step-nav { position:sticky; top:var(--sticky-top); z-index:5; display:flex; gap:.45rem; overflow-x:auto; margin:0 0 2rem; padding:.6rem; border:1px solid var(--line); border-radius:14px; background:rgba(251,253,252,.96); }.step-nav a { flex:0 0 auto; padding:.45rem .7rem; border-radius:9px; color:var(--brand-dark); text-decoration:none; font-weight:700; }.step-nav a:hover { background:var(--mint); }.locked-banner,.export-warning { border:1px solid #d7c388; border-radius:14px; background:#fff9e8; padding:1rem; margin-bottom:1rem; }.save-status { min-height:2rem; position:relative; width:max-content; max-width:100%; margin-block-end:.5rem; border-radius:9px; background:var(--mint); color:var(--brand-dark); padding:0 .6rem; }.save-status:empty { padding:0; }
/* Saved, said in the same place the saving was said, and taken back down
   afterwards: a line that stays says «this page has a message» rather than
   «that worked». The tick is drawn, because a typed ✓ is a letter. */
.save-status.is-saved { display:inline-flex; align-items:center; gap:var(--gap-glyph); background:#e7f4ef; color:#1a6a51; font-weight:700; }
.save-status.is-saved .action-icon { width:15px; height:15px; }.decision-section { scroll-margin-top:calc(var(--sticky-top) + 7rem); padding:2rem 0; border-top:1px solid var(--line); }.section-heading { display:flex; align-items:end; justify-content:space-between; gap:2rem; margin-bottom:1rem; }.section-heading h2 { font-size:clamp(1.6rem,3vw,2.4rem); margin-bottom:0; }.section-heading>p { color:var(--muted); max-width:500px; }.decision-form fieldset,.criteria-form fieldset,.choice-form fieldset { border:0; margin:0; padding:0; }.form-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.7rem 1rem; }.decision-form label:not(.check-label),.choice-form label:not(.check-label),.add-choice-form label:not(.check-label) { display:flex; flex-direction:column; gap:.25rem; font-weight:700; }.decision-form input,.decision-form select,.choice-form select,.choice-form textarea,.add-choice-form select,.criteria-form select { width:100%; border:1px solid #bdcbe4; border-radius:10px; padding:.68rem; font:inherit; background:#fff; }.criteria-form label { display:grid; grid-template-columns:1fr minmax(140px,.35fr); align-items:center; gap:1rem; padding:.7rem 0; border-bottom:1px solid var(--line); }.criteria-form button { margin-top:1rem; }.add-choice-form { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:end; gap:1rem; margin-bottom:1rem; }.ranked-list { display:grid; gap:1rem; }.choice-editor { display:grid; grid-template-columns:76px minmax(0,1fr); border:1px solid var(--line); border-radius:20px; background:#fff; overflow:hidden; }.choice-rank { display:flex; flex-direction:column; align-items:center; gap:.7rem; padding:1.3rem .6rem; background:var(--mint); }.choice-rank>strong { font-size:2rem; color:var(--brand-dark); }.choice-rank>div { display:grid; gap:.4rem; }.icon-button { width:38px; height:38px; border:1px solid #c5d5ec; border-radius:10px; background:#fff; color:var(--brand-dark); font-size:1.1rem; cursor:pointer; }.icon-button:disabled { opacity:.35; cursor:not-allowed; }.choice-form { padding:1.3rem; }.choice-heading { display:flex; justify-content:space-between; gap:1rem; }.choice-heading h3 { margin-bottom:.2rem; font-size:1.45rem; }.choice-heading p { color:var(--muted); }.score-ring { flex:0 0 92px; height:92px; display:flex; flex-direction:column; align-items:center; justify-content:center; border-radius:50%; background:var(--mint); color:var(--brand-dark); }.score-ring strong { font-size:1.7rem; line-height:1; }.score-ring span { font-size:.72rem; }.choice-controls { display:grid; grid-template-columns:1fr 220px; gap:.5rem 1rem; align-items:center; }.score-details { margin:1rem 0; border:1px solid var(--line); border-radius:12px; padding:.7rem; }.score-details summary { cursor:pointer; font-weight:700; }.score-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:.7rem; padding-top:.8rem; }.choice-footer { display:flex; justify-content:space-between; gap:1rem; align-items:center; margin-top:.7rem; color:var(--muted); }.text-button { border:0; background:none; color:#8a241b; font:inherit; cursor:pointer; text-decoration:underline; }.comparison-wrap { overflow-x:auto; border:1px solid var(--line); border-radius:16px; background:#fff; }.comparison-table { border-collapse:collapse; min-width:900px; width:100%; }.comparison-table th,.comparison-table td { padding:.8rem; border-bottom:1px solid var(--line); text-align:right; }.comparison-table th:first-child { position:sticky; right:0; min-width:190px; background:#fff; }.comparison-table small { display:block; color:var(--muted); }.audit-warning { border:1px solid #ecdcb4; border-radius:11px; background:#fff9e8; padding:.8rem; }/* A list that is not finished is not a list with errors in it. This line used
   to be set in the danger red at ۸۰۰, which turned «you still have two things
   to tick» into an alarm; it is the page's own ink now, and the ready state
   keeps the brand's. */
.audit-state { font-weight:700; color:var(--ink); }.audit-state.ready { color:var(--brand-dark); }.audit-list { list-style:none; padding:0; }.audit-list li { border-top:1px solid var(--line); padding:.7rem 0; }/* The label on each row: what is still to do, and what is worth knowing. Both
   are quiet chips rather than coloured shouts — the row's own sentence is what
   carries the meaning, and thirty red words down one column read as thirty
   things having gone wrong. */
.audit-list .error strong,.audit-list .warning strong { display:inline-block; margin-inline-end:.45rem; border-radius:7px; padding:.05rem .45rem; font-size:.78rem; font-weight:700; }
.audit-list .error strong { background:#fdefe0; color:#7d4a12; }.audit-list .warning strong { background:#eef1f5; color:#55637a; }.export-head { padding-bottom:1rem; }.export-list { margin:2rem 0; padding:0; list-style:none; counter-reset:choices; }.export-list li { display:grid; grid-template-columns:1fr auto; gap:.4rem 1rem; padding:1rem; border-bottom:1px solid var(--line); counter-increment:choices; }.export-list li div { display:flex; flex-direction:column; }.export-list li div strong:before { content:counter(choices) '. '; color:var(--brand); }.export-list li code { font-size:1rem; }.export-list li small { grid-column:1/-1; color:var(--muted); }.copy-output { width:100%; border:1px solid var(--line); border-radius:10px; padding:1rem; resize:vertical; }.export-audit { margin:2rem 0 5rem; }
.criteria-form [data-criterion] { display:grid; grid-template-columns:minmax(0,1fr) minmax(140px,.35fr) auto; align-items:end; gap:1rem; padding:.7rem 0; border-bottom:1px solid var(--line); }.criteria-form [data-criterion] label { display:flex; flex-direction:column; gap:.25rem; padding:0; border:0; }.criteria-form [data-criterion] input { width:100%; border:1px solid #bdcbe4; border-radius:10px; padding:.68rem; font:inherit; background:#fff; }.criteria-form [data-criterion] input[readonly] { border-color:transparent; background:transparent; font-weight:700; }
.review-boundary { border:1px solid #d7c388; border-radius:14px; background:#fff9e8; padding:1rem; }.review-plan-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:1rem; padding-bottom:5rem; }.review-plan-card { display:flex; flex-direction:column; align-items:start; }.review-plan-card h2 { margin-bottom:.3rem; }.review-plan-card p { color:var(--muted); }.review-plan-card small { margin-bottom:1rem; color:var(--muted); }.review-choice-list { display:grid; gap:.7rem; padding:0; list-style:none; counter-reset:review-order; }.review-choice-list li { counter-increment:review-order; display:flex; align-items:start; justify-content:space-between; gap:1rem; border:1px solid var(--line); border-radius:14px; background:#fff; padding:1rem; }.review-choice-list li div:first-child { display:flex; flex-direction:column; }.review-choice-list li strong:before { content:counter(review-order) '. '; color:var(--brand); }.review-choice-list li span,.review-choice-list li small { color:var(--muted); }.review-choice-list li p { margin:.5rem 0 0; }.review-choice-list li>div:last-child { display:flex; gap:.35rem; }.proposal-form label:not(.check-label),.comment-form label:not(.check-label),.collaboration-grid label:not(.check-label),.proposal-list label:not(.check-label) { display:flex; flex-direction:column; gap:.3rem; font-weight:700; }.proposal-form textarea,.comment-form textarea,.comment-form select,.collaboration-grid select,.collaboration-grid input,.proposal-list input { width:100%; border:1px solid #bdcbe4; border-radius:10px; padding:.68rem; font:inherit; background:#fff; }.comment-list { display:grid; gap:.7rem; margin-bottom:1rem; }.comment-list article { border:1px solid var(--line); border-radius:12px; background:#fff; padding:.8rem 1rem; }.comment-list header,.proposal-list header { display:flex; justify-content:space-between; gap:1rem; }.comment-list header span,.proposal-list header span { color:var(--muted); font-size:.85rem; }.comment-list p { margin:.4rem 0 0; }.collaboration-grid { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:1.5rem; padding-bottom:2rem; }.collaboration-grid .panel>label { margin-bottom:.8rem; }.collaborator-list { display:grid; gap:.7rem; }.collaborator-form { display:grid; grid-template-columns:1fr 1fr auto auto; align-items:center; gap:.5rem; margin-top:.8rem; }.invitation-list { display:grid; border:1px solid var(--line); border-radius:14px; background:#fff; }.invitation-list article { display:flex; justify-content:space-between; align-items:center; gap:1rem; padding:.8rem 1rem; border-bottom:1px solid var(--line); }.invitation-list article:last-child { border-bottom:0; }.invitation-list article div { display:flex; flex-direction:column; }.invitation-list small { color:var(--muted); }.proposal-list { display:grid; gap:1rem; }.order-comparison { display:grid; grid-template-columns:1fr 1fr; gap:1rem; }.order-comparison>div { border:1px solid var(--line); border-radius:12px; padding:1rem; }.order-comparison h3 { font-size:1rem; }.invite-page form { margin-top:1rem; }
.privacy-boundary { border:1px solid #d7c388; border-radius:14px; background:#fff9e8; padding:1rem; margin-bottom:1.5rem; }.sharing-grid { display:grid; grid-template-columns:minmax(300px,.9fr) minmax(0,1.1fr); gap:1.5rem; padding-bottom:3rem; }.share-controls fieldset { border:0; padding:0; margin:0 0 1.2rem; }.share-controls legend { font-weight:800; margin-bottom:.5rem; }.share-controls select { width:100%; border:1px solid #bdcbe4; border-radius:10px; padding:.68rem; font:inherit; background:#fff; }.share-preview-shell>h2 { font-size:1.3rem; }.share-card { width:min(100%,620px); margin-inline:auto; border:1px solid #c9d8ee; border-radius:20px; background:#fff; box-shadow:none; padding:clamp(1.4rem,4vw,2.5rem); }.share-card.template-square { aspect-ratio:1/1; }.share-card.template-portrait { aspect-ratio:4/5; }.share-card.template-landscape { aspect-ratio:16/9; }.share-card h1,.share-card h3 { font-size:clamp(1.6rem,4vw,2.7rem); margin-bottom:1rem; }.share-card section { border-top:1px solid var(--line); padding-top:.7rem; margin-top:.7rem; }.share-card section p,.share-card section ul { margin-bottom:0; }.share-card small { display:block; color:var(--brand-dark); margin-top:1rem; }.share-preview-shell>label,.share-preview-shell input { width:100%; }.public-share-list { display:grid; gap:.7rem; }.public-share-list article { display:flex; justify-content:space-between; align-items:center; }.public-share-list article>div:first-child { display:flex; flex-direction:column; }.public-share-list small { color:var(--muted); }.public-share-page { display:grid; grid-template-columns:minmax(0,1.3fr) minmax(280px,.7fr); gap:2rem; align-items:start; padding:5rem 0; }.public-share-card { width:100%; aspect-ratio:auto !important; min-height:560px; }.public-share-cta { position:sticky; top:calc(var(--sticky-top) + 1rem); }.public-share-cta h2 { font-size:1.7rem; }.public-share-cta .text-link { display:block; margin-top:1rem; }.catalog-card-actions { display:flex; align-items:center; flex-wrap:wrap; gap:.8rem; margin-top:1rem; }.public-proof { padding:1rem 0 5rem; }
@media (max-width:900px) { .catalog-filters { grid-template-columns:1fr 1fr; }.catalog-filters label { align-self:end; }.sharing-grid { grid-template-columns:1fr; } }
@media (max-width:760px) { .feature-grid,.workspace-grid,.review-plan-grid,.collaboration-grid,.order-comparison,.sharing-grid,.public-share-page { grid-template-columns:1fr; }.workspace-head,.section-heading { flex-direction:column; align-items:start; padding-top:2.5rem; }.feature-grid article { grid-column:1; }.site-header { min-height:66px; }.site-header .main-nav { gap:.55rem; font-size:.82rem; flex-wrap:wrap; justify-content:end; }.catalog-filters,.form-grid,.score-grid,.choice-controls { grid-template-columns:1fr; }.add-choice-form { grid-template-columns:1fr; }.choice-editor { grid-template-columns:48px minmax(0,1fr); }.choice-rank { padding:.8rem .25rem; }.choice-heading { flex-direction:column; }.score-ring { width:80px; height:80px; }.criteria-form label { grid-template-columns:1fr; }.step-nav { margin-inline:-.4rem; }.choice-footer,.comment-list header,.proposal-list header { align-items:start; flex-direction:column; }.collaborator-form { grid-template-columns:1fr; }.invitation-list article,.public-share-list article { align-items:start; flex-wrap:wrap; }.review-choice-list li { align-items:start; }.share-card.template-square,.share-card.template-portrait,.share-card.template-landscape { aspect-ratio:auto; min-height:480px; }.public-share-cta { position:static; } }
/* Paper =====================================================================
   What this used to be: four declarations, no `@page`, and no reset of the
   widths the screen layout sets. A printed list of ۱۵۰ rows came out shifted
   into the left third of the page with the right two-thirds blank and the
   university column cut off — because the sheet did not fit the paper at
   100%, so it got scaled down by hand, and Chrome anchors scaled print output
   at the top-LEFT corner whatever the page's direction is. In an RTL document
   that empties the right side. The fix is not to scale: it is to make the
   sheet fit A4 at its natural size, which is what everything below does.

   `--page-w` is `min(1160px, …)` — a screen measurement, on a page that is
   about 700px wide. Every element that reads it is reset here rather than
   left to resolve against a viewport that does not exist on paper. */
@page { size:A4; margin:14mm 12mm 16mm; }
@media print {
  :root,body { background:#fff; }
  /* No gutter, no fixed viewport, no horizontal overflow: an overflowing
     document is the other way a printed RTL page ends up misaligned. */
  html { scrollbar-gutter:auto; }
  html,body { width:auto; margin:0; padding:0; overflow:visible; }
  main,.site-banner { width:auto; max-width:none; margin-inline:0; }
  /* Screen furniture, and anything that floats over the page. A fixed,
     rotated element is laid out against the page box and can widen it. */
  .site-header,.site-footer,.no-print,.workspace-toolbar,
  .feedback-tab,.feedback-dialog,.toast-region,.to-top,.skip-link,
  .site-banner,.back-link { display:none !important; }
  .export-head { padding:0; }
  .export-warning { break-inside:avoid; }

  /* The sheet's own type. Smaller than the screen's, because a reference
     sheet is read at a desk with the whole row in view rather than at arm's
     length — and because ۱۵۰ rows at screen size is nine pages. */
  body { font-size:10pt; line-height:1.6; }
  .export-head h1 { font-size:15pt; margin:0; }
  .export-track h2 { font-size:12pt; }

  /* `fixed` is what keeps the table on the paper. With `auto`, the two
     `width:1%` columns and five columns of Persian text let the table size
     itself to its content and run off the edge, which is the overflow that
     started all of this. Fixed layout divides the page and wraps. */
  /* No box around the table on paper: an overflow container clips its
     contents to one page, and the table is laid out to the sheet anyway. */
  .export-table-wrap { overflow:visible; }
  .export-table { table-layout:fixed; width:100%; min-width:0; font-size:9pt; }
  .export-table th,.export-table td { padding:.18rem .3rem; word-break:break-word; }
  .export-col-rank { width:7%; }
  .export-col-code { width:13%; }
  /* A header that repeats on every sheet, and rows that are not split across
     two of them: a row broken in half is a row that has to be read twice. */
  .export-table thead { display:table-header-group; }
  .export-table tr { break-inside:avoid; }
  /* Each list starts its own sheet, so the pages of one can be handed over
     without the other. It is the nearest thing print has to two files. */
  .export-track { break-before:page; margin-bottom:0; }
  .export-track:first-of-type { break-before:auto; }

  /* The mark, once at the top, and the address at the foot of every sheet:
     `position:fixed` in print repeats an element on each page. */
  .print-brand.print-only {
    display:flex; align-items:center; gap:.5rem;
    border-bottom:1.5pt solid #1b5e9c; padding-bottom:.35rem; margin-bottom:.6rem;
  }
  .print-brand img { width:26px; height:26px; }
  .print-brand p { margin:0; display:flex; flex-direction:column; line-height:1.35; }
  .print-brand strong { font-size:12pt; color:#173f5f; }
  .print-brand span { font-size:8pt; color:#5f6f89; }
  .print-brand-url { margin-inline-start:auto; font-size:9pt; font-weight:700; color:#1b5e9c; }
  /* Repeated by the printer at the foot of every page the table spans — a
     `position:fixed` footer repeats too, but is placed against the page's
     content box and lands on the last rows of a full page. */
  .export-table tfoot { display:table-footer-group; }
  .export-table tfoot td {
    border-bottom:0; border-top:.75pt solid #cbd7ea;
    padding-top:.25rem; color:#5f6f89; font-size:8pt; text-align:center;
  }
}
@media (prefers-reduced-motion:no-preference) { .button { transition:background-color .18s ease; } }
@media (forced-colors:active) { .button,.status-pill,.panel { border:1px solid CanvasText; }.score-ring { border:2px solid CanvasText; } }

/* Blue product language — shared across the landing page and application. */
/* The gutter is reserved at all times, so locking the page behind a dialog
   never shifts the layout sideways as the scrollbar disappears. */
html { scrollbar-gutter:stable; }
/* Smooth scrolling is granted, never taken back: declared only where motion
   is welcome, so a browser that never matches the query jumps rather than
   glides. */
@media (prefers-reduced-motion:no-preference) { html { scroll-behavior:smooth; } }
/* A dialog is modal: the page behind it must not scroll away underneath. */
html:has(dialog[open]) { overflow:hidden; }
body { color:var(--ink); }
h1,h2,h3,.brand,.button { letter-spacing:0; }
/* The name beside the mark, a step up (owner, 2026-09-08). This is the rule
   that decides it: the declaration in the base block above is overridden here
   unconditionally, so the size lives in one place and this is it. */
.brand { font-size:1.3rem; }
.brand span { display:inline-block; }
.site-header .main-nav>a:not(.button) { border-radius:10px; padding:.4rem .15rem; }
/* One language for everything in the header: links and the account menu take
   the same soft surface on hover, the same ink shift, the same radius. */
.site-header .main-nav>a:not(.button):hover { background:var(--brand-soft); color:var(--brand); }
.button:hover { background:var(--brand-dark); }
.button-quiet { border-color:#c9d8ee; background:var(--brand-soft); box-shadow:none; color:var(--brand-dark); }
.button-quiet:hover { background:#dbe7f9; }
.card-kicker { display:inline-flex; width:max-content; border-radius:999px; background:var(--brand-soft); padding:.28rem .72rem; color:var(--brand-dark); font-size:.88rem; }
/* The fold belongs to the promise and the first action, not to empty space:
   the hero is as tall as what it holds. */
.hero { min-height:0; }
.hero h1 { max-width:26ch; font-weight:850; line-height:1.4; }
.hero .lead { line-height:2; }
.decision-card,.panel { border-color:var(--line); background:var(--surface); box-shadow:none; }
.decision-card { position:relative; overflow:hidden; }
.decision-card li { grid-template-columns:3.25rem 1fr; padding:1.2rem 0; }
.decision-card strong { display:grid; place-items:center; width:2.6rem; height:2.6rem; border-radius:50%; background:var(--brand-soft); color:var(--brand-dark); font-size:.92rem; }
.decision-card span b { display:block; margin-bottom:.1rem; color:var(--ink); font-size:1rem; }
.feature-grid { gap:1.2rem; padding:0 0 4rem; }
.feature-heading { grid-column:1/-1; max-width:700px; margin-bottom:.5rem; }
.feature-heading h2 { font-size:clamp(1.7rem,3vw,2.65rem); margin-bottom:.6rem; }
.feature-heading p { color:var(--muted); }
.feature-grid article { position:relative; overflow:hidden; border:1px solid var(--line); border-radius:18px; background:var(--surface); box-shadow:none; padding:1.65rem; }
.feature-grid article>span { display:grid; place-items:center; width:2.5rem; height:2.5rem; margin-bottom:1rem; border-radius:11px; background:var(--brand-soft); color:var(--brand-dark); }
.how-flow-start .help-text { line-height:1.9; }
.panel input,.panel select,.panel textarea,.catalog-filters select,.catalog-filters input,.decision-form input,.decision-form select,.choice-form select,.choice-form textarea,.add-choice-form select,.criteria-form select,.criteria-form [data-criterion] input,.proposal-form textarea,.comment-form textarea,.comment-form select,.collaboration-grid select,.collaboration-grid input,.proposal-list input,.share-controls select { border-color:#bdcbe4; background:#fff; }
.panel input:focus,.panel select:focus,.panel textarea:focus { border-color:var(--brand); outline:3px solid rgba(27,94,156,.16); }
.claim-draft { border-color:#b5c9f3; background:var(--brand-soft); }
.comparison-wrap,.invitation-list { border-color:var(--line); box-shadow:var(--shadow-sm); }
.step-nav { border-color:var(--line); background:var(--surface); }
.icon-button { display:inline-flex; align-items:center; justify-content:center; padding:0; border-color:#a9bce3; color:var(--brand-dark); font:inherit; line-height:1; vertical-align:middle; }
.icon-button svg { flex:none; }
.icon-button:hover:not(:disabled) { border-color:var(--brand); background:var(--brand-soft); }
.catalog-pagination { display:flex; align-items:center; justify-content:center; flex-wrap:wrap; gap:.75rem 1rem; margin:-2rem 0 5rem; }
.catalog-pagination .button { gap:var(--gap-glyph); }
.catalog-pagination .is-disabled { opacity:.45; pointer-events:none; }
.choice-rank,.score-ring { background:var(--brand-soft); }
.share-card { border-color:#c9d8ee; background:var(--surface); box-shadow:none; }
@media (max-width:760px) {
  .feature-heading { grid-column:1; }
}

.chevron-icon { display:block; width:16px; height:16px; overflow:visible; color:currentColor; }
/* A note about the environment, not a banner about it. It never renders in
   production, and where it does render it is one quiet line above the page
   rather than a filled box the width of the content it is pushing down. */
/* A question with two or three named answers, each with a line explaining it:
   one fieldset shape, wherever it is asked. The card inside it is
   `.selection-card`, which is its own component. */
.selection-cards,.dev-role-options { display:grid; gap:.7rem; min-width:0; margin:1.2rem 0; padding:0; border:0; }
.selection-cards { grid-template-columns:repeat(auto-fit,minmax(14rem,1fr)); margin-block:.9rem 0; }
.dev-role-options legend { margin-bottom:.5rem; font-weight:800; }
/* The card's own layout, said once for every question that uses it: the
   control, then the name of the answer with the line that explains it under
   the name. It used to be written against the fieldset the development
   sign-in draws, so the same card asked anywhere else lost its own shape and
   ran the explanation into the name. */
.selection-card { display:grid; grid-template-columns:auto minmax(0,1fr); align-items:start; gap:.75rem; margin:0; }
.selection-card input[type="radio"],.selection-card input[type="checkbox"] { margin-block:calc((1.8em - 1.25rem) / 2) 0; accent-color:var(--brand); }
.selection-card strong,.selection-card small { display:block; }
.selection-card small { margin-top:.2rem; color:var(--muted); font-weight:400; line-height:1.7; }
.auth-alternative { margin:1.2rem 0 0; text-align:center; }

/* Selection controls
   Keep the native input semantics while owning the geometry and state language.
   Labels provide the 44px interaction target; the visible control stays compact. */
input:is([type="checkbox"],[type="radio"]) {
  appearance:none;
  display:inline-grid;
  place-content:center;
  flex:0 0 auto;
  inline-size:1.25rem;
  block-size:1.25rem;
  min-inline-size:1.25rem;
  margin:0;
  /* The edge is the whole control while nothing is ticked, so it has to clear
     3:1 against the white it is drawn on. #8ea2c4 measured 2.59:1, and at the
     1px this box wears inside a `.check-label` two readers independently
     reported the same thing: «تیک انتخاب دیده نمی‌شود» in the catalog's
     province menu, and «خیلی روشن است و اصلا دیده نمی‌شود» on the exam-group
     list. It was not the tick — a ticked box is brand-filled and vivid — it
     was the empty box being invisible, so there was nothing to read a tick
     against. #6b80a6 measures 3.99:1 and is the same hue one step down. */
  border:2px solid #6b80a6;
  background:#fff;
  color:#fff;
  padding:0;
  vertical-align:-.22em;
  cursor:pointer;
  transition:border-color .16s ease,background-color .16s ease,box-shadow .16s ease,transform .12s ease;
}
input[type="checkbox"] { border-radius:.38rem; }
input[type="radio"] { border-radius:50%; }
input:is([type="checkbox"],[type="radio"])::before {
  content:"";
  transform:scale(0);
  transition:transform .12s ease;
}
/* Whole pixels. The marker was ۸٫۸px inside a ۱۶px box, which centres exactly
   at ۱۰ and paints ۳٫۶px of ring on each side: half-pixel edges, anti-aliased
   asymmetrically, which reads as a dot sitting off-centre in its own circle.
   Every value in this block is now an integer number of pixels. */
input[type="checkbox"]::before {
  inline-size:11px;
  block-size:9px;
  background:currentColor;
  clip-path:polygon(14% 44%,0 59%,39% 100%,100% 18%,82% 0,37% 65%);
}
input[type="radio"]::before {
  inline-size:8px;
  block-size:8px;
  border-radius:50%;
  background:var(--brand);
}
input[type="checkbox"]:checked,
input[type="checkbox"]:indeterminate {
  border-color:var(--brand);
  background:var(--brand);
}
input[type="radio"]:checked { border-color:var(--brand); }
input:is([type="checkbox"],[type="radio"]):checked::before,
input[type="checkbox"]:indeterminate::before { transform:scale(1); }
input[type="checkbox"]:indeterminate::before {
  inline-size:10px;
  block-size:2px;
  border-radius:999px;
  clip-path:none;
}
input:is([type="checkbox"],[type="radio"]):hover:not(:disabled) { border-color:var(--brand); }
input:is([type="checkbox"],[type="radio"]):active:not(:disabled) { transform:scale(.92); }
input:is([type="checkbox"],[type="radio"]):focus { outline:none; }
input:is([type="checkbox"],[type="radio"]):focus-visible {
  border-color:var(--brand);
  outline:1px solid var(--focus);
  outline-offset:2px;
}
input:is([type="checkbox"],[type="radio"]):disabled { border-color:#c5cfdf; background:#eef2f7; color:#a5b2c6; cursor:not-allowed; }
/* The tick greys through currentColor; the dot is painted brand and must be
   told, or a disabled group keeps one vivid answer that reads as clickable. */
input[type="radio"]:disabled::before { background:#a5b2c6; }

.check-label:has(input:disabled),
.selection-card:has(input:disabled) { opacity:.58; cursor:not-allowed; }

.selection-card {
  min-block-size:64px;
  border:1px solid #c9d5e8;
  border-radius:14px;
  background:#fff;
  padding:.8rem .9rem;
  cursor:pointer;
  transition:border-color .16s ease,background-color .16s ease,box-shadow .16s ease,transform .16s ease;
}
.selection-card:hover { border-color:#91a9d6; background:#fbfcff; }
.selection-card:has(input:checked) {
  border-color:var(--brand);
  background:var(--brand-soft);
}

.dev-role-options label { align-items:center; }
.dev-role-options input { margin:0; }
.share-controls .check-label { margin:.25rem 0; }


@media (prefers-reduced-motion:reduce) {
  input:is([type="checkbox"],[type="radio"]),
  input:is([type="checkbox"],[type="radio"])::before,
  .check-label,
  .selection-card { transition:none; }
}
@media (forced-colors:active) {
  input:is([type="checkbox"],[type="radio"]) {
    appearance:auto;
    border:0;
    background:Canvas;
    forced-color-adjust:auto;
  }
  input:is([type="checkbox"],[type="radio"])::before { display:none; }
  .selection-card:has(input:checked) { outline:2px solid Highlight; }
}

/* Catalog data table ----------------------------------------------------- */
.catalog-filters {
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  align-items:end;
  gap:.9rem;
  border-radius:20px;
  padding:1.15rem;
}
.catalog-filters label { display:block; margin:0 0 .32rem; color:#34435e; font-size:.86rem; font-weight:750; }
.catalog-filters input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]),.catalog-filters select { width:100%; min-height:46px; margin:0; border:1px solid #bdcbe4; border-radius:11px; background-color:#fff; padding:.62rem .72rem; font:inherit; }
.catalog-filters input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):hover,.catalog-filters select:hover { border-color:#91a9d6; }
.catalog-search-field { grid-column:span 2; }
.catalog-filter-field { min-width:0; }
.catalog-filter-actions { grid-column:1/-1; display:flex; align-items:center; justify-content:flex-start; gap:.6rem; }
.catalog-filter-actions .button { flex:0 0 auto; min-width:150px; margin:0; white-space:nowrap; }
.catalog-filters[aria-busy="true"] { opacity:.72; }
.active-filter-list { display:flex; align-items:center; flex-wrap:wrap; gap:.45rem; margin:1rem 0; color:var(--muted); font-size:.87rem; }
.active-filter-list>a { display:inline-flex; align-items:center; gap:var(--gap-glyph); min-height:44px; border:1px solid #c9d8f7; border-radius:999px; background:var(--brand-soft); padding:.25rem .7rem; color:var(--brand-dark); text-decoration:none; font-weight:700; }
.active-filter-list>a:hover { border-color:var(--brand); background:#dce8ff; }
.active-filter-list b { font-size:1.15rem; font-weight:500; line-height:1; }
.catalog-result-bar { display:flex; align-items:center; justify-content:space-between; gap:1rem; margin:1.35rem 0 .65rem; }
.catalog-result-bar p { margin:0; color:var(--muted); }
.catalog-result-bar strong { color:var(--ink); font-size:1.08rem; }
.catalog-table-shell { position:relative; max-height:min(72vh,780px); overflow:auto; overscroll-behavior:contain; border:1px solid #cbd7ea; border-radius:18px; background:#fff; box-shadow:var(--shadow-sm); scrollbar-gutter:stable both-edges; }
.catalog-table-shell:focus-visible { outline:1px solid var(--focus); outline-offset:2px; }
.catalog-table-shell[aria-busy="true"] { opacity:.68; }
.catalog-table { width:100%; min-width:1120px; border-collapse:separate; border-spacing:0; font-size:.9rem; line-height:1.65; }
.catalog-table th,.catalog-table td { padding:.72rem .78rem; text-align:start; vertical-align:top; border-block-end:1px solid #e5ebf5; }
.catalog-table tbody tr:nth-child(even) { background:#fafcff; }
.catalog-table tbody tr:hover,.catalog-table tbody tr:focus-within { background:#f1f6ff; }
.catalog-table tbody tr:last-child td { border-block-end:0; }
.catalog-table td strong,.catalog-table td small { display:block; }
.catalog-table td small { margin-top:.12rem; color:var(--muted); }
.table-sort { display:inline-flex; align-items:center; gap:var(--gap-glyph); min-height:40px; color:inherit; text-decoration:none; }
.table-sort:hover { color:var(--brand); }
.table-sort-icon { width:.82rem; height:.82rem; opacity:.28; }
.table-sort.is-active .table-sort-icon { opacity:1; }
.catalog-code { min-width:102px; text-align:start !important; }
.catalog-code code { color:#263751; font-size:.9rem; }
.catalog-field-cell { min-width:190px; }
.catalog-capacity { min-width:92px; font-weight:800; }
.catalog-capacity span { color:var(--muted); font-size:.82rem; font-weight:500; }
/* No `display` here: this is a table cell, and the row of controls inside it
   is what does the laying out (`.row-actions`). This rule used to say
   `display:flex` and was quietly corrected by a later `display:table-cell`. */
.catalog-row-actions { min-width:145px; white-space:nowrap; }
.catalog-row-actions a { display:flex; align-items:center; min-height:40px; color:var(--brand-dark); font-weight:700; text-decoration:none; }
.catalog-row-actions a+a { margin-top:.3rem; color:var(--muted); font-size:.84rem; font-weight:600; }
.catalog-row-actions a:hover { color:var(--brand); }
.row-source,.row-conditions { position:relative; margin-top:.3rem; }
.row-source summary,.row-conditions summary { display:inline-flex; align-items:center; width:max-content; min-height:36px; color:var(--muted); cursor:pointer; font-size:.78rem; font-weight:600; }
.row-source summary::marker,.row-conditions summary::marker { content:""; }
.row-source summary::before,.row-conditions summary::before { content:""; display:inline-block; width:.38rem; height:.38rem; margin-inline-end:.38rem; border-inline-end:1.5px solid currentColor; border-block-end:1.5px solid currentColor; transform:rotate(45deg) translateY(-.12rem); transition:transform .16s ease; }
.row-source[open] summary::before,.row-conditions[open] summary::before { transform:rotate(225deg) translate(-.05rem,-.05rem); }
.row-source small,.row-conditions ul { position:absolute; z-index:8; inset-inline-start:0; top:calc(100% + .25rem); width:min(280px,calc(100vw - 2rem)); margin:0; border:1px solid #c8d6ea; border-radius:10px; background:#fff; box-shadow:var(--shadow-pop); padding:.7rem .8rem; color:#4c5d77; white-space:normal; overflow-wrap:anywhere; }
.row-conditions ul { width:min(380px,calc(100vw - 2rem)); padding-inline-start:1.8rem; }
.catalog-table-empty { padding:3rem !important; text-align:center !important; color:var(--muted); }
.catalog-pagination { margin:1.25rem 0 5rem; }

@media (max-width:1000px) {
  .catalog-filters { grid-template-columns:repeat(3,minmax(0,1fr)); }
  .catalog-search-field { grid-column:span 2; }
  .catalog-filter-actions { grid-column:1/-1; }
}
@media (max-width:600px) {
  .catalog-filters { grid-template-columns:1fr 1fr; gap:.75rem; padding:.9rem; }
  .catalog-search-field,.catalog-filter-actions { grid-column:1/-1; }
  .catalog-filter-actions { align-items:stretch; flex-direction:column; }
  .catalog-result-bar { align-items:flex-start; flex-direction:column; gap:.2rem; }
  .catalog-result-bar p:last-child { display:none; }
  .catalog-table-shell { margin-inline:-.25rem; max-height:68vh; border-radius:14px; }
  .catalog-table { font-size:.86rem; }
  .catalog-table th,.catalog-table td { padding:.65rem .7rem; }
}

/* Visual audit refinements ---------------------------------------------- */
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]),select,textarea {
  transition:border-color .16s ease,box-shadow .16s ease,background-color .16s ease;
}
select { appearance:none; cursor:pointer; }
input::placeholder,textarea::placeholder { color:#68778e; opacity:1; }
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):hover:not(:disabled),select:hover:not(:disabled),textarea:hover:not(:disabled) { border-color:#91a9d6; }
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):disabled,select:disabled,textarea:disabled { border-color:#d7dfeb; background:#eef2f7; color:#7c899e; cursor:not-allowed; }
textarea { resize:vertical; }
.button { transition:background-color .16s ease,border-color .16s ease,box-shadow .16s ease,transform .12s ease; }
.button:active:not(:disabled) { transform:translateY(1px); }
.skip-link:focus-visible,
.site-header a:focus-visible,
.step-nav a:focus-visible,
.catalog-row-actions a:focus-visible,
.table-sort:focus-visible,
.pager-icon:focus-visible,
.pager-page:focus-visible,
details>summary:focus-visible,
input:not([type="hidden"]):focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline:3px solid rgba(27,94,156,.28);
  outline-offset:3px;
}
.brand,.site-header .main-nav>a:not(.button) { min-height:44px; }
.site-header .main-nav>a { display:inline-flex; align-items:center; border-radius:9px; }
.site-header .main-nav>a[aria-current] { background:var(--brand-soft); color:var(--brand); padding-inline:.55rem; }
.actions>a:not(.button) { display:inline-flex; align-items:center; min-height:var(--control-h); }
/* A link that acts on a row is a row control, so it is the row's height. It
   was 44 beside buttons of 38, which is the one row in the product where a
   link and a button sat at two different heights. */
.catalog-card-actions .text-link { display:inline-flex; align-items:center; min-height:var(--action-h); }
.score-details>summary { display:flex; align-items:center; min-block-size:44px; }
.icon-button { width:44px; height:44px; }
.button:disabled { opacity:.48; cursor:not-allowed; box-shadow:none; }
.text-button { display:inline-flex; align-items:center; min-height:var(--action-h); padding-inline:.35rem; border-radius:7px; text-underline-offset:.18em; }
.text-button:hover { background:#fff2f1; }
.workspace-head { padding-block:3.25rem 1.8rem; }
.workspace-head h1 { line-height:1.3; letter-spacing:-.035em; }
.workspace-head>.actions { max-width:520px; justify-content:flex-start; margin-top:0; gap:.65rem; }
.workspace-head>.actions .button { min-height:44px; }
.workspace-head>form,.workspace-head>.button { margin-top:.25rem; }
.message-page h1 { max-width:760px; font-size:clamp(2.2rem,5vw,3.45rem); line-height:1.35; }
.empty { line-height:1.9; }
.empty h2,.empty h3 { margin-bottom:.35rem; color:#34435e; }
.empty p { margin-bottom:0; }
.collaborator-column>h2 { margin-bottom:.8rem; }
.collaborator-empty { min-height:176px; display:flex; flex-direction:column; justify-content:center; }

.claim-draft { display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center; gap:.25rem 2rem; }
.claim-draft>* { grid-column:1; }
.claim-draft>.button { grid-column:2; grid-row:1/6; }
.claim-draft p { margin-bottom:.15rem; }
.claim-draft h2 { margin-bottom:.3rem; }
.comparison-table thead th { background:#edf3ff; color:var(--brand-dark); font-weight:800; }
.comparison-table tbody tr:nth-child(even) { background:#fafcff; }
.comparison-table tbody tr:hover { background:#f1f6ff; }
.success-message { border:1px solid #bfd0f3; }



@media (prefers-reduced-motion:reduce) {
  input:not([type="hidden"]),select,textarea,.button,.row-source summary::before,.row-conditions summary::before { transition:none; }
}
@media (max-width:760px) {
  /* One bar, one line: the brand at one end and the two menus at the other.
     The nav used to drop to a second line and scroll sideways there, which
     cost a whole row of the screen to show one and a half of four links. */
  .site-header { align-items:center; flex-direction:row; gap:.5rem; padding-block:.5rem; }
  .site-header-lead { min-width:0; flex:1 1 auto; }
  .brand span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .workspace-head { gap:1rem; padding-block:2.3rem 1.35rem; }
  .workspace-head>.actions { width:100%; }
  .workspace-head>.actions .button { flex:1 1 145px; }
  .message-page { align-items:stretch; }
  .message-page .button { width:max-content; max-width:100%; }
  .claim-draft { display:flex; align-items:flex-start; flex-direction:column; gap:.35rem; }
  .claim-draft>.button { margin-top:.5rem; }
  }
@media (max-width:460px) {
  .catalog-filters { grid-template-columns:1fr; }
  .catalog-search-field,.catalog-filter-actions { grid-column:1; }
  .catalog-filter-actions .button { width:100%; min-width:0; }
  .actions { gap:.65rem; }
}

/* Decision workspace: low-cognitive-load recommendation flow ------------- */
.minimal-profile-form { padding:clamp(1.15rem,3vw,1.8rem); }
/* Three equal answers on one row and the long list of interests on its own,
   so no cell is left with a control a third of its width. */
.essential-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:1rem 1.1rem; align-items:start; }
.essential-grid label { margin:0; }
.essential-grid label>span,.essential-grid .field-control>span { display:block; margin-bottom:.35rem; color:#34435e; font-weight:800; }
.essential-grid input,.essential-grid select { min-height:46px; margin:0; }
.essential-grid small { display:block; margin-top:.35rem; color:var(--muted); font-size:.85rem; font-weight:400; line-height:1.65; }
.disclosure-body { padding:0 1rem 1.1rem; }
.disclosure-body>.help-text { margin-bottom:.9rem; }
.disclosure-body>.form-grid { margin-block:0 .9rem; }
.primary-flow-action { margin-top:1.2rem; min-height:var(--control-h); }

/* Importance is one quantity, so it reads as one track from «مهم نیست» to
   «زیاد». The level is written out and colour-coded as well, because position
   alone is neither readable at a glance nor available without colour. */
.criteria-card-list { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.75rem; margin-bottom:1.2rem; }
.criteria-form .criterion-card { display:grid; grid-template-columns:minmax(0,1fr) minmax(150px,.55fr); align-items:center; gap:.6rem 1.1rem; min-width:0; border:1px solid var(--line); border-radius:14px; background:#fff; padding:.85rem 1rem; }
.criterion-text { min-width:0; }
.criterion-card strong { display:block; color:var(--ink); font-size:1rem; }
.criterion-card small { display:block; margin-top:.15rem; color:var(--muted); font-weight:400; font-size:.83rem; line-height:1.6; }
.criteria-form .importance-control { display:grid; gap:.2rem; margin:0; border:0; padding:0; }
/* Importance is one quantity, so the scale stays one hue and deepens along it:
   a hue per step made «زیاد» amber -- the colour «لازم» uses -- and said four
   unrelated things about one dial. What was wrong with the old ramp was that it
   was invisible: every step above «مهم نیست» drew the same brand-blue track, so
   the colour coded nothing anyone could see. Each step now sets its own
   `--level`, which the pill and the slider track both read, and «زیاد» fills
   solid so the top of the scale is unmistakable. «مهم نیست» is the flat neutral
   because it is the absence of a setting rather than a step on the scale. Every
   ink clears 4.5:1 on the tint it sits on. */
.criterion-card { --level:var(--brand); }
.criterion-card[data-importance="0"] { --level:#55627a; }
.criterion-card[data-importance="1"] { --level:#4a7bb5; }
.criterion-card[data-importance="2"] { --level:#1b5e9c; }
.criterion-card[data-importance="3"] { --level:#123f73; }

.choice-editor { box-shadow:var(--shadow-sm); }
.choice-form { min-width:0; }
.choice-heading p { margin-bottom:.25rem; }
.reason-chips { display:flex; flex-wrap:wrap; gap:.45rem; margin:.65rem 0 .9rem; padding:0; list-style:none; }
.reason-chips li { border:1px solid #c8d6f0; border-radius:999px; background:#f5f8ff; padding:.22rem .65rem; color:#344e80; font-size:.82rem; }
.choice-footer { padding-top:.8rem; border-top:1px solid var(--line); }
.recommendation-empty { text-align:center; padding-block:2.5rem; }
/* The two confirmations are one group, so they carry the group's spacing
   rather than a margin each: `1rem 0 !important` on both put a full line
   between two boxes that ask the same kind of question. */
.final-confirmations { display:grid; gap:.5rem; margin:1rem 0; }
.final-confirmation { width:100%; margin:0 !important; border:1px solid #bfd0f3; border-radius:12px; background:#f5f8ff; padding:.8rem 1rem; font-weight:700; }
.final-confirmation input { flex:none; }

@media (max-width:980px) {
  .essential-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .criteria-card-list { grid-template-columns:1fr; }
}
@media (max-width:680px) {
  .essential-grid { grid-template-columns:1fr; }
  .criteria-form .criterion-card { grid-template-columns:minmax(0,1fr); }
  .choice-editor { grid-template-columns:44px minmax(0,1fr); }
  .choice-form { padding:.9rem; }
}

/* Shared design-system primitives --------------------------------------- */
.card-kicker {
  display:block;
  width:auto;
  border-radius:0;
  background:transparent;
  padding:0;
  color:var(--brand-dark);
}
.button { background:var(--brand); box-shadow:none; }
.button:hover { background:#154d80; }
.button-quiet { border-color:#c9d8ee; background:var(--brand-soft); color:var(--brand-dark); }
.button-quiet:hover { background:#dbe7f9; }

/* Native select: one recognizable stroked chevron in the RTL trailing area. */
select:not([multiple]) {
  appearance:none;
  background-color:#fff;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='m5 7.5 5 5 5-5' stroke='%23173f5f' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:left var(--chevron-inset) center;
  background-size:1rem;
  padding-inline-end:calc(var(--chevron-inset) * 2 + 1rem) !important;
}

/* Every disclosure uses the exact same icon, size, and closed/open rotation. */
details>summary { position:relative; list-style:none; cursor:pointer; padding-inline-end:2rem; }
details>summary::-webkit-details-marker { display:none; }
details>summary::marker { content:""; }
details>summary::after,
.row-source summary::before,.row-conditions summary::before {
  content:"";
  position:absolute;
  inset-inline-end:.25rem;
  top:50%;
  display:block;
  width:1rem;
  height:1rem;
  margin:0;
  border:0;
  background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='m5 7.5 5 5 5-5' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='m5 7.5 5 5 5-5' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
  transform:translateY(-50%);
  transition:transform .16s ease;
}
details[open]>summary::after,
.row-source[open] summary::before,.row-conditions[open] summary::before { transform:translateY(-50%) rotate(180deg); }
.row-source summary,.row-conditions summary { position:relative; padding-inline-end:1.35rem; }
.row-source summary::after,.row-conditions summary::after { display:none; }
.row-source summary::before,.row-conditions summary::before { inset-inline-end:0; width:.8rem; height:.8rem; }

.field-group { min-width:0; margin:0 0 1rem; border:0; padding:0; }
.field-group legend { margin-bottom:.45rem; color:#34435e; font-weight:800; }
.choice-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.35rem; }
.form-feedback { min-height:1.75rem; margin:1rem 0 0; border-radius:10px; padding:.55rem .75rem; }
.form-feedback:empty { min-height:0; margin:0; padding:0; }
/* Colour is never the only signal: a shape rides with it, so the state
   still reads under colour blindness or a grayscale screen. */
.form-feedback[data-state="error"],.form-feedback[data-state="success"] {
  margin-top:1rem; display:flex; align-items:center; gap:.4rem;
}
.form-feedback[data-state="error"] { background:#fff0ee; color:#8a241b; }
.form-feedback[data-state="success"] { background:#e9f0fb; color:#173f5f; }
.form-feedback[data-state="error"]::before,.form-feedback[data-state="success"]::before {
  content:""; flex:none; display:block; width:15px; height:15px; background:currentColor;
}
.form-feedback[data-state="error"]::before {
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' y1='8' x2='12' y2='12'/%3E%3Ccircle cx='12' cy='16' r='.5' fill='black'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' y1='8' x2='12' y2='12'/%3E%3Ccircle cx='12' cy='16' r='.5' fill='black'/%3E%3C/svg%3E") center/contain no-repeat;
}
.form-feedback[data-state="success"]::before {
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center/contain no-repeat;
}

.multi-select { position:relative; min-width:0; }
.multi-select>summary { display:flex; align-items:center; min-height:46px; overflow:hidden; border:1px solid #bdcbe4; border-radius:11px; background:#fff; padding:.62rem 2.5rem .62rem .72rem; color:var(--ink); font-weight:500; white-space:nowrap; }
.multi-select>summary span { overflow:hidden; text-overflow:ellipsis; }
.multi-select[open]>summary { border-color:var(--brand); }
/* A filter that is doing something looks like it. Eight identical boxes in one
   grid say nothing about which of them narrowed the list, and the only thing
   that did was «۲ مورد انتخاب شده» in the same small type as «همه استان‌ها»
   beside it. The answered one takes the brand edge and the soft brand surface
   this product already uses for a chosen state, and its own label goes to the
   brand ink — so «کدام فیلترها فعال‌اند؟» is answered by looking, not reading.
   The surface is the signal; nothing gets a second coloured edge. */
.ordered-multi-select.is-answered>.multi-select>summary {
  border-color:var(--brand); background:var(--brand-soft);
  color:var(--brand-dark); font-weight:700;
}
.multi-select-field:has(.ordered-multi-select.is-answered)>.control-label { color:var(--brand-dark); }
.multi-select-panel { position:absolute; z-index:20; inset-inline:0; top:calc(100% + .35rem); max-height:310px; overflow-x:hidden; overflow-y:auto; border:1px solid #c9d8ee; border-radius:13px; background:#fff; box-shadow:var(--shadow-pop); padding:.4rem; }
.multi-select-panel [data-multi-options] { display:grid; min-width:0; }
.multi-select-panel .check-label { display:flex; min-width:0; margin:0; padding:.45rem .5rem; font-weight:500; }
.multi-select-panel .check-label input[type="checkbox"] { width:1.25rem !important; min-width:1.25rem; height:1.25rem !important; min-height:0 !important; padding:0; }
.multi-select-panel .check-label span { min-width:0; overflow-wrap:anywhere; }
.multi-select-search { position:sticky; top:-.5rem; z-index:1; display:block !important; margin:-.5rem -.5rem .35rem !important; background:#fff; padding:.5rem !important; }
.multi-select-search input { width:100%; min-height:var(--control-h) !important; margin:0 !important; border:1px solid #bdcbe4; border-radius:9px; padding:.45rem .65rem !important; font:inherit; }
.multi-select-empty { margin:.5rem; color:var(--muted); font-size:.85rem; }
.control-label { display:block; margin-bottom:.35rem; color:#34435e; font-weight:800; }
.catalog-filters .control-label { margin-bottom:.32rem; font-size:.86rem; font-weight:750; }
.ordered-multi-select { display:grid; gap:.45rem; min-width:0; }
.selected-pill-list { display:flex; align-items:center; flex-wrap:wrap; gap:.45rem; min-width:0; }
.selected-pill-list[hidden] { display:none; }
/* A token is a label, not a control panel: no inner dividers, and the grip and
   the remove button stay quiet until the token is hovered or focused. The whole
   token is the mouse drag surface; touch drags start on the grip alone, so a
   finger resting on a token still scrolls the page. */
.selected-pill { display:inline-grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:.1rem; max-width:100%; min-height:34px; border:1px solid #cfdbea; border-radius:10px; background:#f4f7fc; padding:2px; color:#22364f; user-select:none; transition:border-color .16s ease,background-color .16s ease,box-shadow .16s ease,opacity .16s ease; }
.selected-pill:hover,.selected-pill:focus-within { border-color:#9fb6dc; background:#eef3fb; }
.selected-pill.is-dragging { border-color:var(--brand); background:#fff; box-shadow:0 6px 18px rgba(23,63,95,.16); opacity:.9; }
.selected-pill-label { min-width:0; overflow-wrap:anywhere; padding-inline:.25rem; font-size:.86rem; font-weight:600; line-height:1.5; }
.pill-drag-handle,.selected-pill-remove { display:grid; place-items:center; width:26px; min-width:26px; height:26px; margin:0; border:0; border-radius:7px; background:transparent; padding:0; color:#6b7d9c; font:inherit; line-height:0; cursor:pointer; transition:color .16s ease,background-color .16s ease; }
.pill-drag-handle { cursor:grab; touch-action:none; }
.pill-drag-handle:active { cursor:grabbing; }
.selected-pill:hover .pill-drag-handle,.selected-pill:focus-within .pill-drag-handle { color:#5d7197; }
.selected-pill:hover .selected-pill-remove,.selected-pill:focus-within .selected-pill-remove { color:#8a241b; }
.pill-drag-handle .action-icon,.selected-pill-remove .action-icon { width:16px; height:16px; flex:none; }
.pill-drag-handle:hover,.selected-pill-remove:hover { background:#fff; }
.pill-drag-handle:focus-visible,.selected-pill-remove:focus-visible { position:relative; z-index:1; }
/* Coarse pointers keep a target the guidance asks for, without the token
   growing to the same size on a mouse-driven screen. */
@media (pointer:coarse) {
  .selected-pill { min-height:44px; }
  .pill-drag-handle,.selected-pill-remove { width:36px; min-width:36px; height:36px; color:#5d7197; }
}
.selection-order-hint { margin:0; color:var(--muted); font-size:.78rem; line-height:1.75; }
.selection-count { margin:.05rem 0 0; color:var(--muted); font-size:.8rem; }
.multi-select-panel .check-label.is-selected-option { display:none; }
.filtered-select-status { margin:0; color:var(--muted); font-size:.8rem; line-height:1.7; }
.filtered-select-status:empty { display:none; }
.character-count { display:block; margin-top:.25rem; color:var(--muted); font-size:.76rem; font-weight:500; text-align:end; }


.status-pill { border-radius:7px; background:#e4f3ec; color:#1a6a51; }
.status-available { background:#e4f3ec; color:#1a6a51; }
.status-unavailable { background:#fff0ee; color:#a03d34; }
.status-unknown { background:#fff6df; color:#8b6018; }
.reason-chips { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.35rem 1rem; }
.reason-chips li { border:0; border-radius:0; background:transparent; padding:.1rem 0; color:#42536b; }
.reason-chips li::before { content:""; display:inline-block; width:.42rem; height:.42rem; margin-inline-end:.45rem; border-radius:50%; background:var(--brand); vertical-align:middle; }

.feature-grid article:nth-of-type(2)>span { background:#fff1dc; color:#8c4d09; }
.feature-grid article:nth-of-type(3)>span { background:#f0ebfa; color:var(--violet); }
.decision-card li:nth-child(2)>strong { background:#fff1dc; color:#8c4d09; }
.decision-card li:nth-child(3)>strong { background:#f0ebfa; color:var(--violet); }

@media (max-width:520px) {
  .choice-grid,.reason-chips { grid-template-columns:1fr; }
  .brand span { font-size:.88rem; }
  .brand img { width:36px; height:36px; }
}

/* Catalog table and shared pagination ----------------------------------- */
.catalog-table-shell {
  max-height:none;
  overflow:visible;
  border-radius:16px;
  scrollbar-gutter:auto;
}
.catalog-table { min-width:0; table-layout:fixed; }
.catalog-table td { overflow-wrap:anywhere; }
.catalog-code,.catalog-field-cell,.catalog-capacity,.catalog-row-actions { min-width:0; }
.catalog-paging-row { display:flex; align-items:center; justify-content:space-between; gap:1rem; margin:.85rem 0; }
.catalog-paging-bottom { margin:1.15rem 0 4rem; }
.catalog-pagination { display:flex; align-items:center; justify-content:flex-end; gap:.35rem; margin:0; }
.page-size-control { display:flex; align-items:center; gap:.55rem; color:var(--muted); font-size:.86rem; }
.page-size-control select { width:auto; min-height:44px; border:1px solid #bdcbe4; border-radius:9px; padding:.35rem .55rem; font:inherit; }
.page-window { display:flex; gap:.25rem; }
.pager-icon,.pager-page { display:grid; place-items:center; width:44px; height:44px; border:1px solid #c9d8ee; border-radius:9px; background:#fff; color:var(--brand-dark); text-decoration:none; font:inherit; font-weight:750; cursor:pointer; }
/* A page number is as wide as its digits. A catalogue of this size reaches four
   of them, and a fixed square clipped «۱۳۳۴» to «۱۳۳». The square is the floor,
   not the ceiling, and the figures are tabular so the row does not jitter as the
   reader pages through it. */
.pager-page { width:auto; min-width:44px; padding-top:calc(var(--numeral-shift) * 2); padding-inline:.5rem; font-variant-numeric:tabular-nums; }
.pager-icon:hover,.pager-page:hover { border-color:var(--brand); background:var(--brand-soft); }
.pager-page.is-current { border-color:var(--brand); background:var(--brand); color:#fff; }
.pager-icon.is-disabled { opacity:.35; pointer-events:none; }
.page-dialog { width:min(420px,calc(100% - 2rem)); border:1px solid var(--line); border-radius:16px; padding:0; box-shadow:var(--shadow-pop); }
.page-dialog::backdrop { background:rgba(13,28,43,.38); }
.page-dialog form { display:grid; gap:1rem; padding:1.4rem; }
.page-dialog .dialog-heading { display:flex; align-items:center; justify-content:space-between; gap:1rem; }
.page-dialog h2 { margin:0; font-size:1.35rem; }
.page-dialog #page-number { width:100%; min-height:48px; margin:0; border:1px solid #bdcbe4; border-radius:10px; padding:.65rem; font:inherit; }

@media (max-width:820px) {
  .catalog-table-shell { border:0; background:transparent; box-shadow:none; }
  .catalog-table,.catalog-table tbody { display:block; }
  .catalog-table thead { display:none; }
  .catalog-table tr { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.65rem 1rem; margin-bottom:1rem; border:1px solid var(--line); border-radius:14px; background:#fff !important; padding:1rem; box-shadow:none; }
  .catalog-table td { display:block; min-width:0; border:0; padding:0; }
  .catalog-table td::before { content:attr(data-label); display:block; margin-bottom:.12rem; color:var(--muted); font-size:.76rem; font-weight:750; }
  .catalog-table td.catalog-table-empty { display:block; grid-column:1/-1; }
  .catalog-row-actions { display:block !important; grid-column:1/-1; border-top:1px solid var(--line) !important; padding-top:.65rem !important; }
  .catalog-row-actions .row-actions { gap:.35rem; }
  .row-source summary,.row-conditions summary { min-height:44px; }
  .row-source small,.row-conditions ul { position:static; width:100%; margin-top:.25rem; box-shadow:none; }
}
@media (max-width:620px) {
  .selected-pill-list { display:grid; grid-template-columns:1fr; }
  .selected-pill { width:100%; }
  .catalog-paging-row { align-items:stretch; flex-direction:column; }
  .catalog-pagination { justify-content:center; }
  .page-size-control { justify-content:space-between; }
  .page-window .pager-page:not(.is-current) { display:none; }
}
/* Phones keep the same two-column row card as tablets: stacking every cell in
   one column made a single row 672px tall and the first result page 29,000px
   long. Two columns measure 482px per row at 360px wide. */


/* Two admission lists, each in its own section -------------------------- */
/* The two are registered separately and the Islamic Azad list is optional, so
   they are stacked rather than set side by side: reading them as a pair would
   suggest one submission with two halves. */
.track-panel { border:1px solid var(--line); border-radius:16px; background:#fff; box-shadow:none; padding:1.15rem; }
.track-head { display:flex; align-items:baseline; justify-content:space-between; flex-wrap:wrap; gap:.25rem 1rem; margin-bottom:.85rem; }
.track-count { margin:0; color:var(--muted); }
.track-count strong { color:var(--ink); font-size:1.15rem; }
.track-remaining { margin:0; color:var(--muted); font-size:.88rem; }
.track-remaining.is-full { color:#8b6018; font-weight:700; }
.search-dialog .workspace-dialog-body input[type="search"] { width:100%; min-height:46px; border:1px solid #bdcbe4; border-radius:11px; padding:.62rem .72rem; font:inherit; background:#fff; }
.offering-results { display:grid; gap:.5rem; margin:.7rem 0 0; padding:0; list-style:none; }
.offering-results li { display:flex; align-items:center; justify-content:space-between; gap:1rem; border:1px solid var(--line); border-radius:12px; background:#fbfcff; padding:.6rem .75rem; }
.offering-results .button { flex:none; }
.track-choices { display:grid; gap:.5rem; margin:0; padding:0; list-style:none; counter-reset:track-choice; }
.track-choice { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:start; gap:.75rem; border:1px solid var(--line); border-radius:14px; background:#fff; padding:.7rem .8rem; }
.track-choice:hover,.track-choice:focus-within { border-color:#a9bde8; background:#fbfcff; }
/* The rank, which is also the control that changes it ------------------------
   One number per row. It used to be this box plus a text input at the far end
   of the strip holding the same digits, so every row said its position twice
   and spent a full-size field saying it the second time. Pressing this opens an
   input in place (see `openRankEditor` in decision-workspace.js).

   It is a button, so it is already focusable and already announced; the input
   that replaces its label is the same size, so nothing on the row moves when it
   opens. Its height is the strip's own control height, which is what makes the
   number, the score chip and the six buttons one row of one size. */
.track-rank {
  /* One shrinkable track, so the input that opens inside it cannot push the
     button wider than the number did — a rank box that changed width on press
     shifted the whole row under the cursor. `2.75rem` is three digits, which
     is the widest rank a list of this capacity reaches, so the column of ranks
     also lines up down the list instead of stepping in at rank ۱۰۰. */
  display:grid; grid-template-columns:minmax(0,1fr); place-items:center;
  width:2.75rem; min-width:2.75rem; height:var(--action-h);
  padding:0 .3rem;
  border:1px solid transparent; border-radius:9px;
  background:var(--brand-soft); color:var(--brand-dark);
  font:inherit; font-size:.85rem; font-weight:800; font-variant-numeric:tabular-nums;
  line-height:1;
  cursor:pointer;
}
.track-rank:hover:not(:disabled) { border-color:var(--brand); }
.track-rank:disabled { cursor:default; }
/* The input takes the button's whole box, so opening the editor changes what
   the number is sitting in and not where it sits.
   `!important` on the size, the way `.multi-select-search input` already does
   it: the page-wide field rules set a 44px min-height and a 12.8px inline
   padding on every input, and an input that is standing in for a 34px chip is
   not one of the fields those rules are about. Without them the button grew to
   the 20-character intrinsic width of a bare text input — 241px in a strip
   234px wide. */
.track-rank>.track-rank-input {
  width:100%; min-width:0;
  height:100% !important; min-height:0 !important;
  margin:0; border-radius:8px;
  /* No edge and no fill of its own: the button around it is already the box,
     and a second border inside the first read as a field dropped into a chip.
     What says «this is now editable» is the caret and the selected digits.
     `!important` for the same reason the size above needs it — the page-wide
     `input:not([type=hidden]):not([type=checkbox]):not([type=radio])` rule
     scores (0,3,1) against this selector's (0,2,1) and would otherwise put its
     own border and white fill back. */
  border:0 !important; background:transparent !important; padding:0 !important;
  color:var(--brand-dark); font:inherit; font-weight:800; font-size:.85rem;
  line-height:1; font-variant-numeric:tabular-nums; text-align:center;
}
/* The focus ring belongs to the button, which is the thing that has a shape.
   Drawn inside it, so opening the editor does not make the row a pixel taller. */
.track-rank:has(>.track-rank-input) { background:#fff; border-color:var(--brand); }
.track-rank>.track-rank-input:focus { outline:none; }
.track-rank:has(>.track-rank-input:focus) { outline:2px solid var(--focus); outline-offset:-2px; }
.track-choice-body { min-width:0; }
.track-choice-body>strong { display:block; overflow-wrap:anywhere; }
.track-choice-body>span,.track-choice-body>small { display:block; color:var(--muted); overflow-wrap:anywhere; }
.track-choice-body>small { font-size:.82rem; }
.track-choice .reason-chips { margin:.4rem 0 .2rem; }
/* Every control on the row, together, at the row's end — which in RTL is its
   left. Said once and nowhere else: `justify-content` was set on this strip in
   five places, and the last two put it back to `flex-start` and pushed the
   delete button away from the rest with `margin-inline-start:auto`, so on a
   phone the row ended with ✕ alone at one side and the other three at the
   other. The controls are one group at one end at every width. */
.track-choice-actions { display:flex; align-items:center; flex-wrap:wrap; justify-content:flex-end; gap:.35rem; }
/* The match, at the size of the rank at the other end of the row -------------
   Both are numeric chips bracketing a choice, so they are one size and one
   weight. This was the page's body size — ۱۶px — and heavier than the
   offering's own name beside it, which made a two-digit number the loudest
   thing on a row that is about a رشته‌محل. The row now steps ۱۶ (name) → ۱۴
   (university) → ۱۳٫۶ (the two numbers) → ۱۲٫۵ (chips): four steps inside a
   ۳٫۵px range, so the order is carried by weight and ink rather than by size. */
.track-score {
  display:grid; place-items:center;
  min-width:2.5rem; height:var(--action-h);
  border-radius:9px;
  /* Grey, not the brand tint. Two numbers bracket every row — the rank and the
     match — and they were the same size, the same weight and the same colour,
     so «۹۹» at one end read as another «۹۹» at the other. The rank is the
     product's own blue because it is the thing the reader sets; the match is a
     number the model reports, and reported numbers are quiet. */
  background:#eef1f5; color:#4c5b73;
  font-size:.85rem; font-weight:800; line-height:1;
  font-variant-numeric:tabular-nums;
}
/* An empty list is a question, not a caption. It says what state it is in, what
   the next move is, and offers that move — three lines and a button, centred,
   in the space the rows would occupy. It used to be one grey sentence pointing
   at a toolbar the reader then had to go and find. */
.track-empty {
  display:grid; justify-items:center; gap:.35rem;
  border:1px dashed #c3d0e6; border-radius:12px;
  color:var(--muted); padding:1.6rem 1.1rem; text-align:center;
}
.track-empty strong { color:var(--ink); font-size:1.02rem; }
.track-empty span { max-width:46ch; }
.track-empty .button { margin-top:.5rem; }
.submission-panel { margin-top:1rem; }
.submission-panel h3 { margin-bottom:.35rem; }
.submission-state.is-selected { border-radius:11px; background:var(--mint); padding:.6rem .9rem; color:#173f5f; }

@media (max-width:620px) {
  .track-choice { grid-template-columns:auto minmax(0,1fr); }
  .track-choice-actions { grid-column:1/-1; }
  /* One line of controls, not two.
     44px is the size a control gets when it stands on its own, and it is the
     right size there. Seven of them in one strip is a different problem: at
     44 the strip measured 339px against the 292px a phone gives it, so every
     one of a hundred and fifty rows carried a second half-empty line with the
     ✕ stranded alone on it — the row was taller than what it said. At 36 the
     whole strip fits on one line, which is worth more to a hand than eight
     extra pixels per button: the targets still clear the 24px floor with room
     to spare, and they keep their spacing.
     Scoped to this strip by a variable rather than restated per control, so
     the buttons, the rank box and the score chip stay one row of one height. */
  .track-choice-actions { --action-h:32px; gap:.25rem; }
  .track-choice-actions .track-score { min-width:2.2rem; }
  .offering-results li { align-items:stretch; flex-direction:column; }
  .offering-results .button { width:100%; }
  /* Keep every reason, but read them as one wrapped line: as a two-column
     list they took a third of each row in a 150-item list. */
  .track-choice .reason-chips { display:flex; flex-wrap:wrap; gap:0 .45rem; }
  .track-choice .reason-chips li { padding:0; font-size:.8rem; }
  .track-choice .reason-chips li::before { display:none; }
  .track-choice .reason-chips li:not(:last-child)::after { content:"،"; }
}
/* Getting the list out of here ----------------------------------------------
   One row of ways out, for the page rather than for each list: a reader who
   wants the file wants both lists in it. The first is the one most readers
   came for, so it is the filled button and the rest are quiet. */
.export-tools {
  display:flex; align-items:center; flex-wrap:wrap; gap:.5rem .6rem;
  margin:0 0 1.5rem;
}
.export-tools .action-icon { width:15px; height:15px; flex:none; }
.export-tools-status { margin:0; font-size:.9rem; }
.export-tools-status.is-copied { color:#1a6a51; font-weight:700; }
.export-tools-status.is-copy-failed { color:#735c25; font-weight:700; }

/* The list, as the table it is ----------------------------------------------
   Five facts per row, read down a column. It was a list whose every row spent
   three lines and put the code at the opposite edge of the page from the name
   it belongs to, which at ۱۵۰ rows is a document rather than a reference
   sheet. The table is also what the text and the CSV are generated from, so
   the page holds one copy of the data instead of two. */
/* The table scrolls inside this, so the page never does. A phone gives the
   five columns about 390px and they need nearer 480. */
.export-table-wrap { overflow-x:auto; }
.export-table {
  width:100%; min-width:28rem; border-collapse:collapse;
  font-size:.92rem;
}
.export-table th,
.export-table td {
  border-bottom:1px solid var(--line);
  padding:.45rem .6rem;
  text-align:start; vertical-align:top;
}
.export-table thead th {
  border-bottom:1px solid #cbd7ea; background:#edf3ff;
  color:var(--brand-dark); font-size:.82rem; font-weight:800; white-space:nowrap;
}
.export-table tbody tr:last-child td { border-bottom:0; }
.export-col-rank { width:1%; white-space:nowrap; font-variant-numeric:tabular-nums; font-weight:700; }
.export-col-code { width:1%; white-space:nowrap; font-variant-numeric:tabular-nums; }
.export-empty { margin:0; color:var(--muted); }

/* Print furniture, invisible on screen. `.print-only` is the product's
   existing name for this and already carries the display rules; these two
   only need their shape, which the print block above gives them. */
.export-track { margin-bottom:2rem; }
/* The buttons that make this list's own files, directly above it. They used
   to be one row at the top of the page producing one file with both lists in
   it, which is a file somebody has to edit before they can use either half. */
.export-track-tools { display:flex; align-items:center; flex-wrap:wrap; gap:.5rem .6rem; margin:0 0 .9rem; }
.export-track-tools .action-icon { width:15px; height:15px; flex:none; }
.export-tools-hint { margin:0; color:var(--muted); font-size:.9rem; }
.export-audit-back { margin:1rem 0 0; }
.export-track h2 { margin-bottom:.15rem; font-size:1.4rem; }
.export-track-count { margin-bottom:.7rem; color:var(--muted); }
.plan-submitted-flag { display:inline-block; margin:0 0 .3rem; border-radius:7px; background:var(--mint); padding:.15rem .6rem; color:#173f5f; font-size:.8rem; font-weight:800; }

/* Selection control: one box, one label, one alignment -------------------- */
/* Two columns, declared. The box can no longer be knocked out of place by a
   page's own `label` rule -- those now exclude this class rather than being
   overridden context by context -- the gap no longer depends on whitespace
   between the tags, and the box is centred on the first line of its own label
   rather than on the whole block, so it never sits low beside a one-line
   answer nor floats mid-paragraph beside a wrapped one. */
.check-label {
  /* Whole pixels: a 1.15rem box is 18.4px wide, so its two halves land on
     opposite sides of the pixel grid and the tick inside it rasterises
     asymmetrically about its own centre. */
  --check-size:18px;
  --check-line:28px;
  display:grid;
  grid-template-columns:var(--check-size) minmax(0,1fr);
  align-items:start;
  gap:.6rem;
  min-block-size:0;
  margin:0;
  border-radius:10px;
  padding:.35rem .4rem;
  line-height:var(--check-line);
  cursor:pointer;
  transition:background-color .16s ease;
}
.check-label input:is([type="checkbox"],[type="radio"]) {
  grid-column:1;
  flex:none;
  width:var(--check-size) !important;
  min-width:var(--check-size);
  height:var(--check-size) !important;
  min-height:0 !important;
  margin:calc((var(--check-line) - var(--check-size)) / 2) 0 0 !important;
  /* The shared 2px, not a hairline. A row in a menu is scanned rather than
     read, and a 1px edge on an 18px box is the thinnest mark on the page at
     the moment it has to be the clearest: it is the only thing saying «this is
     something you can tick». The 11×9 tick still centres inside the 14px the
     border leaves. */
  border-radius:6px;
}
.check-label input[type="radio"] { border-radius:50%; }
.check-label>:not(input) { grid-column:2; min-width:0; }
.check-label:hover { background:var(--brand-soft); }
.check-label:has(input:focus-visible) { background:var(--brand-soft); }
.multi-select-panel .check-label { --check-line:26px; padding:.35rem .45rem; }
.multi-select-panel .check-label input[type="checkbox"] { width:var(--check-size) !important; min-width:var(--check-size); height:var(--check-size) !important; }
@media (pointer:coarse) { .check-label { padding-block:.5rem; } }
@media (prefers-reduced-motion:reduce) { .check-label { transition:none; } }

/* One evidence card, used by the search results and the match list. */
.offering-body { display:grid; flex:1 1 auto; min-width:0; gap:.15rem; }
.offering-title { display:flex; align-items:center; flex-wrap:wrap; gap:.5rem; min-width:0; }
.offering-title strong { overflow-wrap:anywhere; }
.offering-score { flex:none; border-radius:999px; background:var(--mint); padding:.02rem .5rem; color:var(--brand-dark); font-size:.78rem; font-weight:800; }
.offering-where { color:var(--muted); font-size:.88rem; overflow-wrap:anywhere; }
/* What is said *about* an offering: the match and the exam groups, which are
   read the same way — a glance, comparing one row against the next — so they
   are one group and can be placed as one. The score used to live inside the
   title, which made it part of the offering's name for every layout that had
   to place it. */
.offering-meta { display:flex; flex-wrap:wrap; align-items:center; gap:var(--chip-gap); min-width:0; }
.offering-tags { display:flex; flex-wrap:wrap; gap:var(--chip-gap); }
.offering-body>.offering-meta { margin-top:.15rem; }
.offering-tag { border:1px solid var(--line); border-radius:7px; background:#fff; padding:.02rem .42rem; color:#4a5b74; font-size:.76rem; }
.offering-tag.is-national { border-color:#bcd0ee; background:var(--brand-soft); color:var(--brand-dark); font-weight:700; }
.offering-tag.is-azad { border-color:#d5c9ec; background:#f2edfb; color:#4d3c78; font-weight:700; }
.offering-tag.is-code { background:#f4f6fa; color:var(--muted); font-variant-numeric:tabular-nums; }
/* The bidi break between the word and its LTR digits eats the literal space
   on some surfaces; the chip lays the two out itself and draws the gap, so
   «کد» and the number breathe the same everywhere the card appears. */
.offering-tag.is-code { display:inline-flex; align-items:baseline; gap:.3em; }

/* Reviewing candidates in batches --------------------------------------- */
.matches-filters { display:grid; grid-template-columns:repeat(auto-fit,minmax(170px,1fr)); gap:.6rem .8rem; margin-bottom:.9rem; }
.matches-filters label:not(.check-label) { display:grid; gap:.25rem; margin:0; }
.matches-filters label>span { color:#34435e; font-size:.85rem; font-weight:750; }
.matches-filters select { width:100%; min-height:44px; margin:0; border:1px solid #bdcbe4; border-radius:10px; padding:.5rem .7rem; font:inherit; }
.matches-status { margin:0 0 .8rem; color:var(--muted); font-size:.88rem; }
/* A field's matching offerings: the product's own block-that-opens, holding
   the product's own offering card. Nothing here draws a second kind of box. */
.match-groups { display:grid; gap:.6rem; }
.match-group ul { display:grid; gap:.35rem; margin:0; padding:0; list-style:none; }
.match-row { border:1px solid transparent; border-radius:11px; background:#fff; }
.match-row:hover,.match-row:focus-within { border-color:#c5d5ee; }
.match-row .check-label { align-items:flex-start; width:100%; margin:0; padding:.6rem .7rem; }
.matches-foot { display:flex; align-items:center; flex-wrap:wrap; justify-content:space-between; gap:.7rem 1rem; margin-top:1rem; border-top:1px solid var(--line); padding-top:.9rem; }
.matches-pager,.matches-selection { display:flex; align-items:center; gap:.6rem; }
.matches-pager span,.matches-selection span { color:var(--muted); font-size:.88rem; }

/* Either registration is optional, and opting out of either is normal. */

/* Removing a plan is reversible ----------------------------------------- */
.trash-list article { border-top:1px solid var(--line); padding-top:.8rem; }
.trash-list h3 { margin-bottom:.15rem; font-size:1rem; }
.trash-list p { margin-bottom:.35rem; color:var(--muted); font-size:.86rem; }
/* Plan-card actions read as links; only the irreversible one is red. */
.plan-list .catalog-card-actions .text-button { color:var(--brand-dark); font-weight:700; }
.plan-list .catalog-card-actions .text-button:hover { background:var(--brand-soft); }
.plan-list .catalog-card-actions .text-button.plan-trash,
.plan-list .catalog-card-actions .text-button.is-danger { color:var(--danger); }
.plan-list .catalog-card-actions .text-button.plan-trash:hover,
.plan-list .catalog-card-actions .text-button.is-danger:hover { background:#fff2f1; }

/* Spacing pass: one rhythm for every workspace section ------------------- */
.decision-section { padding:1.8rem 0; }
.decision-section:first-of-type { padding-top:1.2rem; }
.section-heading { margin-bottom:.9rem; }
.section-heading>p { margin-bottom:0; }
.workspace-head { padding:2.6rem 0 1.4rem; }
.step-nav { margin-bottom:1.4rem; }
.account-actions { display:flex; align-items:center; flex:none; gap:.8rem; }
.account-name { color:var(--muted); font-weight:700; white-space:nowrap; }

/* Reasons read as one wrapped line at every width. As a fixed two-column grid
   they reserved half a row per choice and left it empty for most of them. */
.reason-chips { display:flex; flex-wrap:wrap; gap:.1rem 1.1rem; }
.track-choice .reason-chips li { padding-inline:0; }

/* Page-level action rows sit beside the title, not two lines below it. */
.workspace-head .actions { gap:.45rem; margin-top:.4rem; }
.workspace-head .actions .button { padding:.55rem .85rem; }
.criteria-form .criterion-card { grid-template-columns:minmax(0,1fr) minmax(160px,.7fr); }

/* Suggestions ------------------------------------------------------------ */
/* A styled listbox in place of the operating-system datalist popup: capped in
   height, scrollable, and part of the page rather than floating over it. */
.suggest-field { position:relative; display:block; min-width:0; }
.suggest-field>input { width:100%; }
.suggest-panel {
  position:absolute; z-index:30; inset-inline:0; top:calc(100% + .3rem);
  max-height:16rem; overflow-y:auto; overscroll-behavior:contain;
  margin:0; padding:.4rem; list-style:none;
  border:1px solid #c9d8ee; border-radius:13px; background:#fff;
  box-shadow:var(--shadow-pop);
}
.suggest-panel[hidden] { display:none; }
.suggest-option { border-radius:8px; padding:.5rem .6rem; color:var(--ink); cursor:pointer; overflow-wrap:anywhere; }
.suggest-option mark { background:#d9e7fa; color:var(--brand-dark); font-weight:800; }
.suggest-option:hover,.suggest-option.is-active { background:var(--brand-soft); }
.suggest-option.is-active { color:var(--brand-dark); font-weight:700; }
@media (pointer:coarse) { .suggest-option { padding-block:.7rem; } }

/* Chosen answers need room before whatever control follows the field. */
.ordered-multi-select>[data-multi-selected]:not([hidden]) { margin-block:.15rem .2rem; }
.how-flow-start #guest-draft-form>.button { margin-top:1.4rem; }

/* Step navigation -------------------------------------------------------- */
/* Each step is numbered in a filled circle, and the step the reader is in is
   marked — the number alone is decoration, the mark is the information. */
.step-nav a { display:inline-flex; align-items:center; gap:.5rem; }
.step-nav a>span {
  display:grid; place-items:center; flex:none; width:1.55rem; height:1.55rem;
  border-radius:50%; background:var(--brand-dark); color:#fff;
  font-size:.82rem; font-weight:800; line-height:1;
  transition:background-color .16s ease;
}
/* Five steps of one task, so five circles of one colour: the mark on the
   current step is the information, and a different hue per step was only
   decoration competing with it. */
.step-nav a>span { background:#c8d3e2; color:var(--brand-dark); }
.step-nav a.is-current { background:var(--brand-soft); color:var(--brand-dark); }
.step-nav a.is-current>span { background:var(--brand); color:#fff; }

/* Account area ----------------------------------------------------------- */
.account-prompt { display:flex; align-items:center; flex-wrap:wrap; justify-content:space-between; gap:.7rem 1rem; margin-bottom:1.5rem; border:1px solid #d7c388; border-radius:14px; background:var(--warm); padding:.85rem 1.1rem; }
.account-prompt span { color:var(--muted); }
.account-form { display:grid; gap:1rem; padding-bottom:4rem; }
.account-form h2 { margin-bottom:.6rem; font-size:1.25rem; }
.account-form .form-grid { margin-bottom:0; }
.account-form .field-group { margin-top:1rem; border:0; padding:0; }
.account-form .field-group legend { margin-bottom:.35rem; color:#34435e; font-weight:800; }
.account-form .help-text { margin:.7rem 0 0; color:var(--muted); font-size:.86rem; }
.exam-results { display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:.8rem; }
.exam-results fieldset { border:1px solid var(--line); border-radius:12px; background:#fff; padding:.7rem .85rem; }
.exam-results legend { padding-inline:.35rem; color:var(--brand-dark); font-weight:800; font-size:.9rem; }
.exam-results label:not(.check-label) { display:grid; gap:.2rem; margin:0 0 .5rem; font-size:.85rem; font-weight:700; }
.exam-results input { margin:0 !important; }
.account-hint { margin:0 0 1rem; color:var(--muted); font-size:.88rem; }
.trash-link { margin:.8rem 0 0; }
.empty-state { display:grid; justify-items:start; gap:.9rem; }
.button.is-danger { border-color:#e6c4bf; background:#fdf1ef; color:var(--danger); }
.button.is-danger:hover { background:#f9e2de; }

/* Persian text is harder to read with a rule through its descenders, so links
   are marked by colour, weight and a hover surface instead of an underline. */
a,.text-link,.text-button,.table-sort { text-decoration:none; }
a:hover,.text-link:hover { text-decoration:none; }
.text-button { text-decoration:none; }
.text-link:hover,.text-button:hover { background:var(--brand-soft); border-radius:7px; }
/* A link inside a paragraph is underlined, except where the link *is* a
   picture of a name: the footer's «فرادرس» is the parent's logotype, and a
   2px rule under it reads as part of the artwork rather than as a link. The
   exception is named here rather than overridden afterwards, because this is
   the rule that decides which links get the underline. */
.account-hint a,.help-text a,.disclosure-body a,p a:not(.button):not(.text-link):not(.faradars-link) {
  color:var(--brand-dark); font-weight:700; box-shadow:inset 0 -2px 0 rgba(27,94,156,.35);
}
.account-hint a:hover,.help-text a:hover,.disclosure-body a:hover,p a:not(.button):not(.text-link):not(.faradars-link):hover {
  box-shadow:inset 0 -2px 0 var(--brand);
}

/* Tooltips ---------------------------------------------------------------- */
/* The offset the script computes is a physical distance from the left edge of
   the window, so the box it moves has to start at that edge. `inset-inline-start`
   resolves to `right` in this right-to-left page, which anchored every tooltip
   to the opposite edge and then added the offset to it: a tip belonging to
   something on the left of the screen ended up a screen-width away from it. */
.app-tooltip {
  position:fixed; top:0; left:0; z-index:90; max-width:22rem;
  border-radius:9px; background:#22364f; padding:.35rem .6rem;
  color:#fff; font-size:.82rem; line-height:1.6; pointer-events:none;
  box-shadow:0 6px 18px rgba(23,63,95,.18);
}
.app-tooltip[hidden] { display:none; }

/* A suggestion list must sit above whatever follows the field it belongs to.
   Raising the field itself, only while its list is open, keeps that true
   whatever the next section happens to be. */
.suggest-field:has(.suggest-panel:not([hidden])),
.multi-select[open] { position:relative; z-index:60; }

/* One calm surface ------------------------------------------------------- */
/* Focus was marked with 3px rings in four different colours, on top of borders
   that were already saturated. The indicator stays clearly visible — it has to
   be — but it is one colour, one weight, and no longer the loudest thing on the
   page. Deciding where to spend the next four years is stressful enough. */
:root { --focus:rgba(27,94,156,.3); --focus-ring:0 0 0 3px rgba(27,94,156,.16); }
/* The quietest thing that still says «this one». Most controls here answer
   focus inside their own box — a field darkens the edge you type inside, a
   quiet control takes its hover surface, a slider brightens its track — and
   this is the fallback for everything that has no such answer of its own. It
   is a hairline rather than a halo, because a ring belongs to whatever was
   touched last rather than to what the reader is looking at. */
*:focus-visible {
  outline:1px solid var(--focus);
  outline-offset:2px;
}
.panel input:focus,.panel select:focus,.panel textarea:focus,
.multi-select[open]>summary,
.suggest-field>input:focus {
  border-color:var(--brand);
  outline:none;
  box-shadow:var(--focus-ring);
}
.check-label input:is([type="checkbox"],[type="radio"]):focus-visible {
  outline:2px solid var(--focus); outline-offset:2px;
}
/* The slider answers focus by brightening its own track, below. */
.importance-control input[type="range"]:focus-visible { outline:none; }
.pill-drag-handle:focus-visible,.selected-pill-remove:focus-visible { outline:2px solid var(--focus); outline-offset:-2px; }

/* Softer surfaces: fewer competing edges, lighter shadows, quieter borders. */
.panel,.decision-card { border-color:#e3e9f2; box-shadow:none; }
.track-panel,.criterion-card,.match-group,.exam-results fieldset { box-shadow:none; }
.suggest-panel,.multi-select-panel { box-shadow:var(--shadow-pop); }
.selected-pill { box-shadow:none; }
.selected-pill.is-dragging { box-shadow:var(--shadow-md); }
.reason-chips li { color:#4a5b74; }
.offering-tag { border-color:#e2e8ef; }
.step-nav { border-color:#e3e9f2; }
.account-prompt { border-color:#e8dcc0; }


/* One offering, in full ---------------------------------------------------- */
.choice-facts-open { margin-top:.2rem; color:var(--brand-dark); font-size:.84rem; font-weight:700; }
.choice-facts-open:hover { background:var(--brand-soft); }
/* The same shell as every other window in this product -----------------------
   `overflow:auto` on the dialog itself put the scrollbar in the window's own
   rounded box. In RTL that gutter is on the left, so the two left corners were
   filled by the scrollbar track and read as square while the right two stayed
   round — a window with two different corner treatments on the same box.
   It is now a column whose *body* scrolls, which is what `.workspace-dialog`
   has always been: the head and the foot stay put, the corners belong to the
   dialog, and nothing paints over them. */
.facts-dialog {
  width:min(34rem,calc(100vw - 2rem)); max-height:min(80vh,44rem);
  border:1px solid var(--line); border-radius:16px; background:#fff; padding:0;
  color:var(--ink); box-shadow:var(--shadow-pop);
  overflow:hidden;
}
.facts-dialog[open] { display:flex; flex-direction:column; }
/* The head is the top of the box, so it carries that box's corners rather than
   needing to stick to something. */
.facts-dialog>.facts-head { flex:0 0 auto; position:static; }
.facts-dialog>.facts-body { flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain; }
.facts-dialog>.facts-editor,
.facts-dialog>.facts-source { flex:0 0 auto; }
.facts-dialog::backdrop { background:rgba(23,63,95,.28); }
.facts-head { display:flex; align-items:start; justify-content:space-between; gap:1rem; border-bottom:1px solid var(--line); background:#fff; padding:1rem 1.2rem; }
.facts-head h2 { margin:0; font-size:1.2rem; }
.facts-body { padding:1rem 1.2rem; }
.facts-list { display:grid; gap:.5rem; margin:0; }
.facts-row { display:grid; grid-template-columns:minmax(7rem,auto) minmax(0,1fr); gap:.6rem; }
.facts-row dt { color:var(--muted); font-size:.86rem; }
.facts-row dd { margin:0; overflow-wrap:anywhere; }
.facts-body h3 { margin:1.1rem 0 .4rem; font-size:1rem; }
.facts-conditions { display:grid; gap:.3rem; margin:0; padding-inline-start:1.1rem; color:#42536b; font-size:.88rem; }
.facts-source { margin:0; border-top:1px solid var(--line); padding:.7rem 1.2rem; color:var(--muted); font-size:.82rem; }

/* Exam results sit with the group they belong to. */
.exam-groups { display:grid; gap:.4rem; }
.exam-group { border:1px solid transparent; border-radius:11px; padding:.1rem .2rem; }
.exam-group:has([data-exam-fields]:not([hidden])) { border-color:var(--line); background:#fbfcfe; padding:.4rem .6rem; }
.exam-group-results { display:grid; grid-template-columns:repeat(auto-fit,minmax(9rem,1fr)); gap:.5rem .7rem; margin:.35rem 0 .2rem; }
.exam-group-results label:not(.check-label) { display:grid; gap:.2rem; margin:0; font-size:.84rem; font-weight:700; }
.exam-group-results input { margin:0 !important; }

/* Quieter labels ---------------------------------------------------------- */
/* A status word and a count are already distinct enough as a filled pill; the
   coloured edge added a second signal for the same thing. */
.status-pill { border-inline-start:0; }
/* The offering code is an identifier, not code: monospace makes Persian-adjacent
   digits sit oddly and implies something the reader can type into a terminal.
   Tabular figures keep a column of codes aligned; the extra letter-spacing that
   went with them only made the digits look stretched, so it is gone. */
.catalog-code { font-variant-numeric:tabular-nums; }

/* Catalog rows that open ------------------------------------------------- */
.catalog-row.is-expanded > td { background:var(--brand-soft); }
.catalog-row-detail > td { border-top:0; background:#fbfcfe; padding-block:0 1rem; }
.catalog-row-detail[hidden] { display:none; }
.row-detail-body { display:grid; gap:.8rem; padding:.2rem .2rem .4rem; }
.row-detail-facts { display:grid; grid-template-columns:repeat(auto-fit,minmax(12rem,1fr)); gap:.5rem 1.2rem; margin:0; }
.row-detail-facts dt { color:var(--muted); font-size:.8rem; }
.row-detail-facts dd { margin:0; overflow-wrap:anywhere; }
.row-detail-conditions h3 { margin:0 0 .3rem; font-size:.92rem; }
.row-detail-conditions ul { display:grid; gap:.25rem; margin:0; padding-inline-start:1.1rem; color:#42536b; font-size:.86rem; }
.row-detail-actions { display:flex; align-items:center; flex-wrap:wrap; gap:.5rem 1rem; }
.row-expand { color:var(--brand-dark); font-weight:700; }
.row-expand:hover { background:var(--brand-soft); }

/* Ordered lists are numbered in the reader's own digits. */
.review-choice-list li strong:before { content:counter(review-order, persian) '. '; }
.export-source { margin:0 0 2rem; color:var(--muted); font-size:.86rem; }

.public-usage { padding:0 0 4rem; color:var(--muted); }
.public-usage strong { color:var(--brand-dark); }

/* The plan list is the page ------------------------------------------------ */
.plan-panel { grid-column:1/-1; }
.plan-panel-head { display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:.6rem 1.5rem; }
.plan-panel-head h2 { margin:0; }

/* Marking an offering ----------------------------------------------------- */
.favorite-toggle { width:34px; height:34px; border-radius:9px; color:#b9c4d3; font-size:1.05rem; }
.favorite-toggle:hover { background:var(--warm); color:var(--warning); }
.favorite-toggle[aria-pressed="true"] { color:var(--warning); }
.add-to-plan { color:var(--brand-dark); font-weight:700; }
.add-to-plan:hover { background:var(--brand-soft); }
.add-to-plan-icon { width:34px; height:34px; border-radius:9px; font-size:1.25rem; line-height:1; }
.add-to-plan-icon:hover { background:var(--brand-soft); }
.favorite-list { display:grid; gap:.5rem; margin:0; padding:0; list-style:none; }
.favorite-list li { display:flex; align-items:start; justify-content:space-between; gap:1rem; border:1px solid var(--line); border-radius:12px; background:#fff; padding:.7rem .85rem; }
.favorite-row-actions { display:flex; align-items:center; flex:none; gap:.4rem; }
/* Most of the state day courses in this catalog are free, so the tag states it
   once, quietly, like every other fact on the card. */
.offering-tag.is-free { border-color:#bcd0ee; color:var(--brand-dark); }
.add-dialog .add-offering { margin:0 0 .8rem; color:var(--muted); }
.add-plan-list { display:grid; gap:.45rem; margin:0; padding:0; list-style:none; }
/* Making the first list, inside the window that needed one. A short form is
   one act: a name and the button that does it, with the name's own label above
   it — the shape every other short form in this product has. */
.add-plan-create { display:grid; gap:.3rem; margin-top:.9rem; }
.add-plan-create[hidden] { display:none; }
.add-plan-create label { font-weight:700; }
.add-plan-create input { width:100%; }
.add-plan-create button { justify-self:start; margin-top:.4rem; }
/* One row per plan: the name, whether the offering is already in it, and the
   one act that flips that. Membership is a state the row wears, not a guess. */
.add-plan-row {
  display:flex; align-items:center; gap:.8rem;
  border:1px solid var(--line); border-radius:12px;
  background:var(--surface); padding:.5rem .55rem .5rem .75rem;
}
.add-plan-row.is-member { border-color:#bcd8cd; background:#f5faf8; }
.add-plan-text { flex:1 1 auto; min-width:0; display:grid; gap:.1rem; }
.add-plan-text strong { font-size:.95rem; overflow-wrap:anywhere; }
.add-plan-text small { display:inline-flex; align-items:center; gap:.3rem; color:var(--muted); font-size:.8rem; }
.add-plan-row.is-member .add-plan-text small { color:var(--success); }
.add-plan-text small .action-icon { width:13px; height:13px; flex:none; }
.add-plan-row .button { flex:none; }
.add-message { margin:.8rem 0 0; color:var(--muted); font-size:.88rem; }

/* One match row says each thing once ------------------------------------- */
.match-row .offering-title strong { font-weight:700; }
.match-row .offering-where { font-size:.9rem; }

/* =======================================================================
   One calm surface, one set of controls
   -----------------------------------------------------------------------
   Everything below is the shared vocabulary the pages draw from: one control
   height, one action-button size, one list surface for every menu, one dialog.
   Nothing here glows, and nothing carries a coloured edge to repeat what its
   own label already says.
   ======================================================================= */
:root {
  /* The one content width every page-level strip shares: header, main,
     footer, and the notice banner all read from here. */
  --page-w:min(1160px,calc(100% - 2rem));
  /* One height for every field and every full-size button, so a row of mixed
     controls lines up without each one being told separately. */
  --control-h:44px;
  /* One size for the square controls that act on a row. */
  --action-h:38px;
  --radius-control:11px;
  /* One distance from the trailing edge for every chevron in a boxed control:
     the native menu, the product's own listbox, and a disclosure that is a box
     rather than a line of running text. */
  --chevron-inset:1rem;
  --radius-surface:16px;
  --radius-row:10px;
  /* One rhythm for the vertical space between page-level sections, so a
     reader feels one system moving down the page instead of each section
     having negotiated its own gap. */
  --space-1:.5rem; --space-2:.75rem; --space-3:1.25rem; --space-4:2rem; --space-5:3rem;
  /* One height for the header everywhere it renders, instead of a value
     re-declared per breakpoint. */
  --header-h:72px;
  /* One label, one small thing beside it. A control's own glyph sits closer
     than a filled badge does: a glyph is a stroke and reads as part of the
     label, a badge is a surface and needs air outside its own edge or the two
     fills run together and read as one word. Both numbers are the ones already
     dominant in this file — .button's icon gap, and the gap the capacity and
     save-state chips use — named once so that the next component does not pick
     a seventh value, and so that the thing that holds a label and its badge
     apart is a gap on a flex row rather than a margin on the badge. A margin
     leaves the badge sitting on the text baseline, which is where «ناتمام»
     came to touch «مشخصات من». */
  --gap-glyph:.4rem;
  --gap-badge:.5rem;
}

/* Fields and buttons share a height and a corner radius ------------------- */
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="range"]),
select,
.multi-select>summary,
.select-trigger,
.button {
  min-height:var(--control-h);
  border-radius:var(--radius-control);
}
/* Text never touches the edge of the box it is typed into — nor anything
   drawn inside it. A field that prepends or appends something of its own (the
   content engine's search box puts a magnifier at the inline start and a clear
   control at the inline end) says how much room that takes through these two
   properties rather than trying to out-specify this rule: four :not() clauses
   put it beyond the reach of a component's own `.search-box input[type=search]`
   selector, and the magnifier was being drawn on top of the first letter of
   the placeholder and of everything the reader then typed. */
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="range"]),
textarea {
  padding-block:.6rem;
  padding-inline-start:var(--field-pad-start,.8rem);
  padding-inline-end:var(--field-pad-end,.8rem);
}
.icon-button,.favorite-toggle,.add-to-plan-icon,.row-expand {
  width:var(--action-h); height:var(--action-h);
  min-width:var(--action-h); min-height:var(--action-h);
  flex:none; padding:0;
  border:1px solid #c9d8ee; border-radius:var(--radius-row);
  background:#fff; color:var(--brand-dark); font-size:1.05rem; line-height:1;
}
.icon-button:hover:not(:disabled),.row-expand:hover:not(:disabled) { border-color:var(--brand); background:var(--brand-soft); }
.icon-button.is-danger { color:var(--danger); }
.icon-button.is-danger:hover:not(:disabled) { border-color:#e0b6b0; background:#fdf1ef; }
/* One red, wherever the irreversible action is. */
.text-button.is-danger { color:var(--danger); }
.text-button.is-danger:hover { background:#fdf1ef; }
.row-expand .chevron-icon { transition:transform .16s ease; }
.row-expand[aria-expanded="true"] .chevron-icon { transform:rotate(180deg); }
@media (pointer:coarse) {
  :root { --action-h:44px; }
}
/* A narrow viewport is a phone whatever the pointer media feature says: a
   desktop browser at 390px reports `fine`, and the row controls stayed 38px
   there. The size a row control needs follows the width of the screen it is
   on, not the hardware the page happens to be rendered by. */
@media (max-width:760px) {
  :root { --action-h:44px; }
}

/* A row of actions is a row, wherever it sits: the same gap, the same
   alignment, the same control size, in a table cell or in a list item. */
.row-actions { display:flex; align-items:center; flex-wrap:wrap; gap:.3rem; }
/* Nothing in a row of controls carries a vertical margin of its own: the row
   places them, and a margin on one of them is that control disagreeing with
   the row about where its own line is. */
.row-actions>*,.row-actions>*>* { margin-block:0; }
/* The cell is an ordinary cell. Laying the cell itself out as a flex box took
   it out of the row's shared height and left it a different size from every
   other column in the same row. */
.catalog-row-actions { display:table-cell; white-space:nowrap; }
.catalog-row-actions a+a { margin-top:0; }
.favorite-row-actions,.catalog-card-actions { gap:.35rem; }

/* Menus: one list surface for every choice ------------------------------- */
/* The native select stays in the form and out of sight; what the reader sees,
   hovers and arrows through is the same list surface as the multi-select and
   the suggestion field. */
.select-shell { position:relative; display:block; min-width:0; }
/* The size is declared !important for the same reason the padding beside it
   already was: this is one class, and any page rule that says `width` for its
   own selects (`.catalog-filters select`, `.page-size-control select`) is more
   specific and wins. The rows-per-page control was the proof — its hidden
   select kept its own 142px content width inside an 86px shell, and since it
   is positioned from the inline start, the surplus hung 25px past the edge of
   a 390px screen and put the whole catalog into horizontal scroll. A control
   that is not on the page must not be able to size the page. */
.select-native {
  position:absolute; inset-inline-start:0; inset-block-start:0;
  width:1px !important; height:1px !important; min-height:0; max-width:1px;
  margin:0; padding:0 !important;
  border:0; opacity:0; pointer-events:none; overflow:hidden; clip-path:inset(50%);
}
.select-trigger {
  display:flex; align-items:center; justify-content:space-between; gap:.6rem;
  width:100%; min-width:0;
  border:1px solid #c9d8ee; background:#fff;
  padding:.55rem var(--chevron-inset);
  color:var(--ink); font:inherit; font-weight:500; text-align:start;
  cursor:pointer;
  transition:border-color .16s ease,box-shadow .16s ease;
}
.select-trigger::after {
  content:""; flex:none; width:1rem; height:1rem;
  background:var(--brand-dark);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='m5 7.5 5 5 5-5' stroke='black' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='m5 7.5 5 5 5-5' stroke='black' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
  transition:transform .16s ease;
}
.select-shell.is-open .select-trigger::after { transform:rotate(180deg); }
.select-trigger:hover:not(:disabled) { border-color:#91a9d6; }
.select-shell.is-open .select-trigger { border-color:var(--brand); box-shadow:var(--focus-ring); }
.select-shell.is-placeholder .select-value { color:var(--muted); }
.select-trigger:disabled { border-color:#dde4ee; background:#f2f5f7; color:#7c899e; cursor:not-allowed; }
.select-trigger:disabled::after { background:#7c899e; }
.select-value { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

.option-panel {
  position:absolute; inset-inline:0; top:calc(100% + .3rem);
  display:flex; flex-direction:column; max-height:18rem;
  border:1px solid #c9d8ee; border-radius:13px; background:#fff;
  box-shadow:var(--shadow-pop); padding:.4rem;
}
.option-panel.is-above { top:auto; bottom:calc(100% + .3rem); }
.option-panel[hidden] { display:none; }
.select-search { padding:.1rem .1rem .35rem; }
.select-search[hidden] { display:none; }
.select-search input { width:100%; min-height:38px !important; margin:0 !important; border:1px solid #d7e0ec; border-radius:9px; padding:.35rem .6rem !important; font:inherit; }
.option-list { display:grid; gap:.1rem; min-height:0; overflow-y:auto; overscroll-behavior:contain; margin:0; padding:0; list-style:none; }
.option-row {
  display:flex; align-items:center; gap:.5rem;
  border-radius:var(--radius-row); padding:.5rem .6rem;
  color:var(--ink); cursor:pointer; overflow-wrap:anywhere;
}
.option-row:hover,.option-row.is-active { background:var(--brand-soft); }
.option-row.is-selected { color:var(--brand-dark); font-weight:700; }
.option-row.is-selected::after {
  content:""; flex:none; margin-inline-start:auto; width:.8rem; height:.65rem;
  background:var(--brand);
  clip-path:polygon(14% 44%,0 59%,39% 100%,100% 18%,82% 0,37% 65%);
}
.option-empty { margin:.4rem .6rem; color:var(--muted); font-size:.86rem; }
@media (pointer:coarse) { .option-row { padding-block:.7rem; } }

/* The three menus share the row language, so a choice looks the same wherever
   it is offered. */
.suggest-option,.multi-select-panel .check-label { border-radius:var(--radius-row); }
.multi-select-panel .check-label:hover { background:var(--brand-soft); }

/* A menu belongs on top of whatever follows the field it opens from —
   including the pagination bar under the catalog filters. Raising the field
   only while its list is open keeps the rest of the page flat. */
.select-shell.is-open,
.suggest-field:has(.suggest-panel:not([hidden])),
.multi-select[open],
.catalog-filter-field:has(.select-shell.is-open),
.catalog-filter-field:has(.multi-select[open]),
.catalog-search-field:has(.suggest-panel:not([hidden])),
.page-size-control:has(.select-shell.is-open),
.matches-filters label:has(.select-shell.is-open) { position:relative; z-index:60; }
.catalog-paging-row { position:relative; z-index:1; }

/* Dialogs ----------------------------------------------------------------- */
.app-dialog {
  width:min(30rem,calc(100vw - 2rem));
  border:1px solid var(--line); border-radius:var(--radius-surface);
  background:#fff; color:var(--ink); padding:0;
  box-shadow:var(--shadow-pop);
}
.app-dialog::backdrop { background:rgba(23,63,95,.28); }
.app-dialog .dialog-heading,.page-dialog .dialog-heading {
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
  border-bottom:1px solid var(--line); padding:.9rem 1.2rem;
}
.app-dialog h2 { margin:0; font-size:1.2rem; }
.app-dialog .dialog-body,.app-dialog>form { display:block; padding:1.1rem 1.2rem 1.2rem; }
.app-dialog .dialog-body label:not(.check-label) { display:block; margin:0 0 .35rem; font-weight:700; }
.app-dialog .dialog-body input:not([type="checkbox"]):not([type="radio"]) { width:100%; margin:0; }
.app-dialog .help-text { margin:.5rem 0 0; font-size:.86rem; }
.dialog-actions { display:flex; align-items:center; flex-wrap:wrap; gap:.6rem; margin-top:1.1rem; }
.dialog-actions .button { flex:0 0 auto; }
/* A question about destroying something reads as a heading and a sentence,
   with the answer that destroys named rather than called OK. */
.confirm-dialog h2 { margin:0 0 .45rem; font-size:1.15rem; }
.confirm-dialog .confirm-message { margin:0; color:#42536b; }
.confirm-dialog>form { padding:1.2rem; }

/* A link or quiet button that carries its own glyph is a row, and the space
   between the glyph and the words is that row's gap. Scoped with :has() so a
   .text-link sitting inside a sentence stays inline and keeps wrapping with
   the text around it. */
.text-link:has(>svg),.text-button:has(>svg) {
  display:inline-flex; align-items:center; gap:var(--gap-glyph);
}

/* Optional and required, said next to the field --------------------------- */
/* This one keeps an inline margin rather than becoming a flex row, because
   it has two hosts: a label, where it follows the name, and a <legend>,
   where it sits INSIDE a sentence («گروه‌های آزمایشی [لازم] و نتیجه
   هرکدام [اختیاری]») and a flex row would break the line into items. It
   needs the space on BOTH sides for that second host: with a start margin
   only, the word after the badge was touching its fill. */
.field-badge {
  display:inline-block; margin-inline:var(--gap-badge);
  border-radius:999px; background:#eef1f5; padding:.22rem .5rem;
  color:#5f6f89; font-size:.74rem; font-weight:700; line-height:1; vertical-align:middle;
}
.field-badge.is-required { background:var(--brand-soft); color:var(--brand-dark); }
.label-text { display:block; }

/* A group of fields that stays on the page -------------------------------- */
/* These used to hide behind a disclosure. Every one of them changes what the
   lists hold, so they are visible, and each says whether it is needed. */

/* Page heading and the tools that belong to the page ---------------------- */
.workspace-head { align-items:flex-start; gap:1.25rem; }
.workspace-head-text { min-width:0; }
/* The way back is a control, and used to be drawn as a caption: muted grey,
   no edge, no underline, sitting above a heading. Two readers said the same
   thing about it — «نحوه برگشت به صفحات قبلی خیلی واضح نیست» and «بازگشت از
   صفحه مشخصات خیلی واضح نیست» — and neither of them was failing to see it;
   they were failing to read it as something to press. It keeps its size and
   its place; what it gains is an edge and the product's own ink, which is the
   difference between a label and a control. `--radius-control`, not a pill:
   pills are selection tokens here. */
.back-link {
  display:inline-flex; align-items:center; gap:.35rem;
  min-height:34px; margin-bottom:.5rem;
  border:1px solid var(--brand-line); border-radius:var(--radius-control);
  background:var(--surface); padding-inline:.7rem .55rem;
  color:var(--brand-dark); font-size:.88rem; font-weight:700;
}
.back-link:hover { border-color:var(--brand); background:var(--brand-soft); }
.back-link .chevron-icon { width:.85rem; height:.85rem; }
/* Four things you can do with this plan, as one quiet strip beside the title
   rather than four filled buttons arguing with the heading. */
.page-tools {
  display:flex; align-items:center; flex-wrap:wrap; flex:none; gap:.15rem;
  margin-top:.4rem; border:1px solid var(--line); border-radius:12px;
  background:#fff; padding:.25rem;
}
.page-tools a {
  display:inline-flex; align-items:center; min-height:36px;
  border-radius:9px; padding:.3rem .7rem;
  color:var(--brand-dark); font-size:.9rem; font-weight:700; white-space:nowrap;
}
.page-tools a:hover { background:var(--brand-soft); }

/* The list is arranged by its owner, and says so -------------------------- */
/* One chip on the head line rather than a row of its own: a state on one side,
   the way out of it on the other, joined by a hairline so the pair reads as one
   control. It sits beside the density switch because both answer «how am I
   reading this list right now». */
/* What this list is in, and the way out of it. They used to be two halves of
   one pill, which drew the half that reports a state exactly like the half
   that acts: same border, same surface, same height, no edge between them but
   a hairline. They are two things now, and only one of them is a button. */
.track-arranged { display:inline-flex; align-items:center; flex-wrap:wrap; gap:.5rem; width:auto; margin:0; }
/* A sentence about the list, in the ink a sentence takes, and beside it a
   button that says what it does. The two used to be a noun and a noun, drawn
   at the same weight, which reads as two options with one of them chosen. */
.track-arranged-state {
  display:inline-flex; align-items:center; gap:.35rem; min-height:var(--action-h);
  color:var(--muted); font-size:.84rem; font-weight:700;
}
.track-arranged-state svg { color:var(--success); }
/* The mark says «this is how it is», not «drag me»: the grip means a thing can
   be dragged everywhere else on this page, and this label cannot be. */
.track-arranged-state svg { flex:none; width:13px; height:13px; }
.track-arranged-reset:disabled { opacity:.5; cursor:progress; }
@media (max-width:600px) { .track-arranged-state span { display:none; } }

/* Footer ------------------------------------------------------------------
   Three zones in one row: the brand + newsletter column is wider, and the
   destinations split into two labeled, vertically-stacked columns instead of
   a floating link line — a category to scan, not a shelf to hunt across. */
.site-footer { display:grid; gap:0; margin-top:var(--space-5); border-top:1px solid var(--line); padding:var(--space-4) 0 var(--space-3); }
.footer-grid {
  display:grid; grid-template-columns:1.3fr 1fr 1fr; gap:var(--space-4) var(--space-5);
  align-items:start;
}
.footer-brand-col { display:flex; flex-direction:column; align-items:flex-start; gap:.35rem; }
.footer-brand { gap:.55rem; font-size:1rem; }
.footer-brand img { flex:none; }
.footer-tagline { margin:0; color:var(--muted); font-size:.85rem; }
/* Same fact, same words and same size as the header's attribution; here it
   carries the link out to FaraDars, which is why the foot of the page no
   longer repeats it. */
.footer-attribution { margin:0; }
.footer-attribution a { color:var(--brand-dark); font-weight:700; text-decoration:none; }
.footer-attribution a:hover { color:var(--brand); }
/* One column per destination group, stacked — each link its own hover/focus
   target, the same soft-fill language the header nav already uses. */
.footer-col { display:flex; flex-direction:column; align-items:flex-start; gap:.2rem; }
.footer-col-title { margin:0 0 .35rem; color:var(--ink-3); font-size:.8rem; font-weight:800; }
.footer-col a {
  display:inline-flex; align-items:center; min-height:32px;
  border-radius:8px; padding:.1rem .45rem; margin-inline:-.45rem;
  color:var(--brand-dark); font-size:.92rem; font-weight:700; text-decoration:none; line-height:1.6;
}
.footer-col a:hover { background:var(--brand-soft); color:var(--brand); }
/* A name inside a sentence: the sentence's own ink and weight, underlined
   because it is a link. Nothing else. A hairline above separates the bottom
   bar from the grid above it, muted like the copyright line it introduces. */
.footer-legal a { color:var(--brand-dark); font-weight:700; text-decoration:none; border:0; }
.footer-legal a:hover { color:var(--brand); }
.footer-legal {
  display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:.35rem 1rem;
  margin:var(--space-4) 0 0; padding-top:var(--space-3); border-top:1px solid var(--line);
  color:var(--muted); font-size:.85rem;
}

/* «Tell me when the ۱۴۰۵ data arrives» -------------------------------------
   Part of the brand column while the site carries the demonstration dataset,
   and the same block as a small panel on the account page. The submit button
   ships hidden (the form only works through fetch) and the script reveals
   it, so the row is laid out to hold it either way. */
/* The row centres its own children, rather than lining them up on a
   baseline and then pulling the icon back to the middle on its own. */
/* `start`, not `center`: against a message that wraps to two lines a centred
   mark floats between them and reads as unattached to either. */

  flex:1 1 230px; width:auto; max-width:340px; margin:0;
  min-height:var(--control-h); border:1px solid var(--field-line); border-radius:10px;
  background:#fff; padding:.55rem .75rem; font:inherit;
  /* The page's own line-height (1.8, for long-form reading) makes a single
     input line taller than the shared control height it is meant to match;
     a control is not a paragraph, so it gets a control's line-height. */
  line-height:1.3;
}
@media (max-width:900px) {
  .footer-grid { grid-template-columns:1fr 1fr; }
  .footer-brand-col { grid-column:1/-1; }
}

@media (max-width:760px) {
  /* Two short lists side by side rather than six rows down the page: it is
     half the height AND it buys every link the 44px a fingertip needs, which
     stacking them could not afford. */
  .footer-grid { grid-template-columns:1fr 1fr; gap:var(--space-3); }
  .footer-brand-col { grid-column:1/-1; }
  .footer-col a { min-height:44px; }
  .site-footer { margin-top:var(--space-4); padding:var(--space-3) 0 var(--space-2); }
  /* The two halves of the legal line are two facts, not a row to justify
     apart: on a narrow screen `space-between` pushed them to opposite edges
     with a gap of nothing between them. */
  .footer-legal { flex-direction:column; align-items:flex-start; gap:.1rem; margin-top:var(--space-3); padding-top:var(--space-2); }
}

/* Spacing: a thing you click has room to be clicked ------------------------ */
.site-header .main-nav>a:not(.button) { padding:.45rem .7rem; }
.site-header .main-nav>a[aria-current] { padding-inline:.7rem; }
.text-link,.add-to-plan,.module-links a,.choice-facts-open {
  display:inline-flex; align-items:center; min-height:var(--action-h);
  border-radius:8px; padding-inline:.5rem;
}
/* A label that names a composite control is one line of text, so its badge sits
   after the words. Left as a flex column it became a full-width grey bar. */
.field-control>label { margin:0; }
.field-control>label { display:block; }
.text-button { min-height:var(--action-h); padding-inline:.5rem; }
.actions { gap:.6rem; margin-top:1.5rem; }
.catalog-filter-actions { gap:.6rem; }
.status-pill { padding:.22rem .62rem; }
.offering-tag { padding:.1rem .48rem; }
.offering-score { padding-block:calc(.08rem + var(--numeral-shift)) calc(.08rem - var(--numeral-shift)); padding-inline:.5rem; }
.track-choice { gap:.65rem .8rem; padding:.75rem .85rem; }
.match-row .check-label { padding:.6rem .75rem; }
.offering-results li { padding:.65rem .8rem; }
.favorite-list li { padding:.75rem .9rem; }
.criteria-form .criterion-card { padding:.9rem 1rem; }
.section-heading { margin-bottom:1rem; }
.decision-section { padding:2rem 0; }
.panel { padding:clamp(1.1rem,3vw,1.6rem); }
.compact-panel { padding:1rem 1.1rem; }
.page-size-control select,.matches-filters select { min-height:var(--action-h); }
.multi-select>summary { padding:.55rem 2.4rem .55rem .8rem; }
.catalog-filters input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]),.catalog-filters select { min-height:var(--control-h); padding:.55rem .75rem; }
.essential-grid input,.essential-grid select,.search-dialog .workspace-dialog-body input[type="search"],.page-dialog #page-number { min-height:var(--control-h); }
.empty-state .button { margin-top:.25rem; }

@media (max-width:760px) {
  .page-tools { width:100%; overflow-x:auto; flex-wrap:nowrap; scrollbar-width:none; }
  .page-tools::-webkit-scrollbar { display:none; }
  .page-tools a { flex:0 0 auto; }
}

/* =======================================================================
   One typeface, one glyph set, one page heading
   -----------------------------------------------------------------------
   The rules below finish the shared vocabulary rather than adding to it:
   every field is in the product's own typeface, every square control holds a
   drawn icon at one size instead of a keyboard character, and every page is
   headed the same way.
   ======================================================================= */

/* A field is written in the page's own typeface ------------------------- */
/* Form controls do not inherit the document font on their own, and the rule
   that said so was repeated per context — so the moment a field appeared
   somewhere new, a dialog above all, it fell back to the browser's default
   face and its default box. Said once, for every control there is. */
input,select,textarea,button,output { font:inherit; }
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="range"]):not(.select-native),
textarea {
  border:1px solid #bdcbe4;
  /* The corner, said here with the border rather than once per component.
     Every box in this product is rounded, but the radius was written into each
     component that has a field — the panel, the filters, the criteria row — so
     a field belonging to no such component came out square: the report window's
     box sat sharp-cornered inside a rounded window, beside rounded buttons.

     11px, not 10, because this rule carries five `:not()`s and therefore beats
     the component rules it is meant to sit under: whatever it says here is what
     every field gets. 11px is what `.multi-select>summary` uses, and that is
     the control standing next to the catalogue's search box — the one pairing
     where a pixel of difference is actually visible. */
  border-radius:11px;
  background:#fff;
  color:var(--ink);
}
/* The two fields that are deliberately not boxes: a rank read back inside a
   criterion row, and the native menu the product's own listbox stands in for. */
.criteria-form [data-criterion] input[readonly] { border-color:transparent; background:transparent; }
.select-native { border:0; }

/* Square controls hold a drawn icon ------------------------------------- */
/* «×» and «+» are letters: they are drawn at whatever size and weight the
   typeface gives them, they sit on the text baseline rather than in the
   middle of the button, and they change shape with the font. Every one of
   them is now the same icon from the sprite, at one size, centred by the
   grid the button already is. */
.icon-button,.favorite-toggle,.add-to-plan-icon,.row-expand,.dialog-close {
  display:inline-grid; place-items:center;
}
/* Whole pixels, not fractions of a rem: a 1.15rem glyph is 18.4px wide, so it
   is centred at a fractional offset and its two halves land on opposite sides
   of the pixel grid. The shapes are symmetric; only the rasterisation was not. */
.icon-button .action-icon { width:20px; height:20px; flex:none; }
.active-filter-list .action-icon { width:16px; height:16px; flex:none; opacity:.7; }
.active-filter-list>a:hover .action-icon { opacity:1; }
.dialog-close .action-icon { width:18px; height:18px; }

/* Closing a dialog ------------------------------------------------------- */
/* The close control is quiet until it is wanted: no box of its own beside the
   heading, and a surface only under the pointer. */
.dialog-close {
  width:var(--action-h); height:var(--action-h);
  min-width:var(--action-h); min-height:var(--action-h);
  flex:none; margin:-.25rem -.35rem -.25rem 0; padding:0;
  border:1px solid transparent; border-radius:var(--radius-row);
  background:transparent; color:var(--muted); cursor:pointer;
}
.dialog-close:hover { background:#eef1f6; color:var(--ink); }
.dialog-close:focus-visible { outline:2px solid var(--focus); outline-offset:2px; }

/* Marking an offering ---------------------------------------------------- */
/* Unmarked is an empty outline in the quietest ink on the page; marked fills
   the same star in. Nothing about the shape changes, so the state reads as
   the mark being made rather than as a different control appearing.

   The star is drawn a little smaller than the other glyphs and lifted a pixel:
   its shape reaches further up and down its own box than a plus or a chevron
   does, so at the same declared size it looks bigger, and its visual centre
   sits below its geometric one. Both numbers are optical, not arithmetic. */
.favorite-toggle .action-icon {
  width:18px; height:18px;
  fill:none; stroke:currentColor;
}
.favorite-toggle { color:#aab6c6; }
/* The star is an outline everywhere, and filling it means one thing only:
   this offering is marked. So the outline is the glyph's default wherever it
   is drawn — the account menu, the dashboard tile, a row's toggle before it is
   pressed — and the single rule below it is what fills one in.

   Stated against the sprite's own id rather than against a list of the places
   the star appears. The list version was `#i-star`, which named a symbol this
   product does not use: the glyph is `i-favorite`, so the two places named
   here silently went back to a solid star, which is the state «marked» drawn
   on a link to a page. Anything that draws this glyph from now on is an
   outline without having to be added to a list. */
svg:has(use[href$="#i-favorite"]) { fill:none; stroke:currentColor; }
.favorite-toggle:hover:not(:disabled) { border-color:#e2c48a; background:var(--warm); color:var(--warning); }
.favorite-toggle[aria-pressed="true"] { border-color:#e2c48a; color:var(--warning); }
.favorite-toggle[aria-pressed="true"] .action-icon { fill:currentColor; }

/* Moving a row up or down is one control with two targets -------------------
   Used by the plan's own rows and by the homepage sample, so the two pages
   cannot show the same act two ways: one outline around the pair, the divider
   in the gap between them, and each button keeping its own press, its own
   focus ring and its own disabled state.

   The divider is the container's own background showing through a 1px gap, not
   a border on the second button. A border there is inside that button's box —
   `box-sizing:border-box` — so its content came out 39.2px against its
   neighbour's 40px and its chevron sat off centre by half a pixel. The two
   buttons are identical boxes now and the line between them belongs to
   neither. */
.move-pair {
  display:inline-flex; flex:none; gap:1px;
  border:1px solid var(--brand-line); border-radius:var(--radius-row);
  background:var(--brand-line); overflow:hidden;
}
.move-pair .icon-button {
  border:0; border-radius:0; background:var(--surface);
}
.move-pair .icon-button:hover:not(:disabled) { background:var(--brand-soft); }
/* Disabled reads as «not now», not as «not a control»: it keeps its shape and
   its place in the pair and gives up only its ink. */
.move-pair .icon-button:disabled { opacity:.32; background:var(--surface); }
.move-pair .icon-button:focus-visible {
  outline:2px solid var(--focus); outline-offset:-2px; z-index:1;
}
/* On the sample the two moves only work once the script reveals them, so
   without it the pair holds nothing — and an empty bordered box is a control
   that is not there. It draws when it has something to draw. */
.move-pair:not(:has(.icon-button:not([hidden]))) { display:none; }

/* Row controls line up as one row ---------------------------------------- */
/* The rank box is one of the controls in this strip, so it is the same height
   and the same corner as the buttons either side of it, and wide enough for
   the three digits a full list reaches. */

/* Optional and required do not look alike --------------------------------- */
/* The two were the wrong way round: «لازم» wore the calm brand tint and
   «اختیاری» the warm one, so the badge that means «you have to answer this»
   was the quieter of the two. Required is warm and forward now, optional is
   the coldest, quietest chip on the page, and a glance separates them without
   either being read. */
.field-badge { background:#eef1f5; color:#55637a; font-weight:700; }
.field-badge.is-required { background:#fdefe0; color:#8a4a0c; font-weight:750; }
/* Not an optionality badge: it reports a state the applicant put the list in. */
.track-arranged .field-badge { background:var(--brand-soft); color:var(--brand-dark); }

/* A hint sits under its field, not against it ----------------------------- */
.filtered-select-status { margin-block-start:.4rem; }
.filtered-select-status:empty { margin-block-start:0; }
.search-dialog .workspace-dialog-body .filtered-select-status { margin-block:.5rem 0; }

/* The table head is one row of one kind of cell ---------------------------- */
/* Six of these cells held a link and two held plain text, so the two sat on a
   different line from the six. Every cell now uses the same block, at the same
   height, and a column that can be sorted but is not sorting says so with both
   arrows rather than with a faded one that looks active. */
.catalog-table thead th { padding-block:.5rem; vertical-align:middle; }
.table-sort,.table-head-label {
  display:flex; align-items:center; gap:.4rem;
  min-height:var(--action-h); padding-inline:.15rem;
  border-radius:8px; color:inherit; font-weight:800; white-space:nowrap;
}
.table-sort { cursor:pointer; }
.table-sort:hover { background:#e2ebfd; color:var(--brand-dark); }
.table-sort-icon { width:16px; height:16px; flex:none; opacity:.45; }
.table-sort:hover .table-sort-icon { opacity:.8; }
.table-sort.is-active { background:#dfe9fc; }
.table-sort.is-active .table-sort-icon { opacity:1; }
.catalog-table thead th:last-child .table-head-label { justify-content:flex-end; }

/* An empty cell says so quietly ------------------------------------------- */
.cell-empty { color:var(--muted); }

/* One page heading ------------------------------------------------------- */
/* Every page is headed the same way: the way back, the title, the sentence
   that says what the page is for, and the things you can do with it as one
   quiet strip beside them. Rows of filled buttons beside a heading argued
   with it on half the pages and were missing from the other half. */
.workspace-head>div:not([class]),.workspace-head-text { min-width:0; }
.page-tools>a,.page-tools>button,.page-tools>form>button {
  display:inline-flex; align-items:center; gap:var(--gap-glyph);
  min-height:36px; margin:0;
  border:0; border-radius:9px; background:transparent;
  padding:.3rem .7rem;
  color:var(--brand-dark); font-size:.9rem; font-weight:700; white-space:nowrap;
  cursor:pointer;
}
.page-tools>form { display:contents; }
.page-tools>a:hover,.page-tools>button:hover,.page-tools>form>button:hover { background:var(--brand-soft); }
.page-tools .is-danger { color:var(--danger); }
.page-tools .is-danger:hover { background:#fdf1ef; }
.page-tools a[aria-current] { background:var(--brand-soft); }

/* Back to the top of the page -------------------------------------------- */
/* Long pages here are genuinely long: a catalogue page of eighty rows and a
   list of a hundred and fifty choices both run far past a window. The control
   appears once the reader is well below the fold and moves focus as well as
   the viewport, so it is a way back for the keyboard too. */
.to-top {
  position:fixed; inset-block-end:1.25rem; inset-inline-end:1.25rem; z-index:80;
  display:grid; place-items:center;
  width:44px; height:44px; padding:0;
  border:1px solid var(--line); border-radius:50%;
  background:var(--surface); color:var(--brand-dark);
  box-shadow:var(--shadow-md); cursor:pointer;
  opacity:0; transform:translateY(.5rem); visibility:hidden; pointer-events:none;
  transition:opacity .18s ease,transform .18s ease,visibility .18s;
}
.to-top.is-visible { opacity:1; transform:none; visibility:visible; pointer-events:auto; }
.to-top:hover { border-color:var(--brand); background:var(--brand-soft); }
.to-top .action-icon { width:20px; height:20px; }
@media print { .to-top { display:none; } }
@media (prefers-reduced-motion:reduce) { .to-top { transition:none; } }
/* Clear of the home indicator: the bottom edge of a phone's viewport is not
   the bottom of its screen. The side it sits on does not change — the
   control belongs at the end of the line on every width. */
@media (max-width:760px) { .to-top { inset-block-end:calc(env(safe-area-inset-bottom,0px) + 1rem); inset-inline-end:1rem; } }

/* Two ways to read a long list -------------------------------------------- */
/* Building a list and ordering one are different jobs. Building it needs the
   evidence on every row; ordering it needs to see thirty rows at once. Compact
   keeps what an order is made from — the rank, the name, the score, the
   controls — and puts everything that justified the row out of the way. */
/* Two named choices in one control, the current one filled. A single button
   whose label flips reads as the state it is in as often as the state it would
   move to, which is why it kept having to be read twice. */
/* Two named choices with the current one filled, wherever the product offers
   a pair of ways to read the same thing: the two list densities, and the two
   ways to read the catalog. A single button whose label flips reads as the
   state it is in as often as the state it would move to, which is why it kept
   having to be read twice. */
.segmented { display:inline-flex; flex:none; border:1px solid #c9d8ee; border-radius:var(--radius-control); background:#f4f7fa; padding:.15rem; }
.segmented button,.segmented a { display:inline-flex; align-items:center; min-height:32px; border:0; border-radius:9px; background:transparent; padding:.25rem .8rem; color:var(--muted); font:inherit; font-size:.86rem; font-weight:700; white-space:nowrap; text-decoration:none; cursor:pointer; transition:background-color .16s ease,color .16s ease; }
.segmented button:hover,.segmented a:hover { background:transparent; color:var(--brand-dark); }
.segmented button[aria-pressed="true"],.segmented a[aria-current] { background:#fff; color:var(--brand-dark); box-shadow:var(--shadow-sm); }
.segmented button:focus-visible,.segmented a:focus-visible { outline:2px solid var(--focus); outline-offset:1px; }
/* Kept as its own name because the workspace script finds the control by it. */
.track-density { flex:none; }

/* The head reads as one row: what the list holds on one side, how it is being
   read on the other, and the arranged badge under both when there is one. */
.track-head { align-items:center; }
.track-head-count { display:grid; gap:.1rem; min-width:0; }
.track-head-count p { margin:0; }

/* Both registrations are optional in the same way, so both say so the same
   way.  used to give one of them a surface of its own. */
.list-optin { margin-bottom:1rem; padding:1rem 1.1rem; }
.list-optin .check-label { margin:0; font-weight:700; }
.list-optin .help-text { margin:.3rem 0 0; padding-inline-start:1.7rem; color:var(--muted); font-size:.86rem; }
.criteria-actions { margin-top:1.2rem; }


/* A note is a mark on the control that opens it --------------------------- */
/* The row already carries a control for «جزئیات و یادداشت»; a note is a dot on
   that control rather than a second thing to look at further up the row. */
.choice-facts-open.has-note { position:relative; border-color:#bcd0ee; }
.choice-facts-open.has-note::after {
  content:""; position:absolute; inset-block-start:4px; inset-inline-end:4px;
  width:.4rem; height:.4rem; border-radius:50%; background:var(--brand);
}

/* One home, said once ----------------------------------------------------- */
/* The city carries its province, so there is one field to fill instead of two
   that have to agree with each other. */

/* Filtering the catalog --------------------------------------------------- */
/* Each picker is one cell of a fixed grid and stays that size however many
   answers it holds: it says only how many. The answers are chips in the row
   underneath, where they are read once and removed one at a time. Six pickers
   that each listed their own selections grew the panel into a page of its own
   the moment any of them held more than one. */
/* The search leads on a line of its own, because typing a name is how most
   people arrive; the seven pickers under it are one grid of equal cells, so
   none of them is visually the important one. */
.catalog-filters { grid-template-columns:repeat(4,minmax(0,1fr)); align-items:start; }
.catalog-filters .catalog-search-field { grid-column:1/-1; }
.catalog-filter-field,.catalog-search-field { min-width:0; }
.catalog-filters .multi-select>summary { width:100%; }
.catalog-filters [data-multi-select] { display:block; min-width:0; }
/* The chosen values are not repeated under the picker they came from. */
.catalog-filters [data-multi-selected],.catalog-filters [data-multi-order-hint] { display:none; }
/* A picker holding an answer says so without opening. */
.catalog-filters .multi-select>summary:has(+ .multi-select-panel) { min-width:0; }
/* center, not baseline: the chips are 44px pills and the label beside them
   is one line of text, and a baseline lines up their two typefaces rather
   than their two boxes. */
.active-filter-list { align-items:center; margin:.85rem 0 0; }
.active-filter-list>a { min-height:38px; }
.active-filter-lead { flex:none; font-weight:750; }

/* How many, how many per page, and which page, on one line. */
.catalog-control-bar { display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:.6rem 1.5rem; margin:1.35rem 0 .75rem; }
.catalog-result-count { margin:0; color:var(--muted); }
.catalog-result-count strong { color:var(--ink); font-size:1.15rem; }
/* The whole the filtered count is a part of. It follows the count with a space
   of its own, because it is a second fact about the same list rather than more
   of the first one; the noun is repeated so the phrase reads on its own when
   the cycle name beside it is dropped on a narrow screen. */
.catalog-result-total::before { content:"\00a0"; }

/* The course type in the catalog's own table, told apart by what it costs.
   The same warm/cold pair the offering card's chips use, at the size of the
   line it already occupies — it is a fact about the row, not a badge added to
   it, so it stays the second line of its own cell. */
.catalog-course { display:inline-block; border-radius:6px; padding:.02rem .35rem; font-weight:600; }
.catalog-course.is-free { background:#e2f2ea; color:#1a6a4c; }
.catalog-course.is-tuition { background:#fdf4e6; color:#7d4a12; }
.catalog-control-bar .catalog-paging-row { flex:1 1 auto; justify-content:flex-end; gap:1rem; margin:0; }
@media (max-width:760px) {
  .catalog-control-bar { align-items:stretch; flex-direction:column; gap:.5rem; }
  .catalog-control-bar .catalog-paging-row { justify-content:space-between; }
  .catalog-result-cycle { display:none; }
}

/* A row opens ------------------------------------------------------------- */
/* The row is the target, so it says so: the pointer over any part of it is the
   pointer over the control that opens it. Everything inside that is itself a
   control keeps its own cursor and its own click. */
.catalog-row { cursor:pointer; }
.catalog-row a,.catalog-row button,.catalog-row label,.catalog-row input { cursor:auto; }
.catalog-row a,.catalog-row button { cursor:pointer; }
.catalog-row:hover { background:#eef4ff; }

/* Eight columns: code, field, university, place, level, capacity, difficulty,
   and the row's own controls.

   `table-layout` is fixed here, so these are not hints — they are the whole
   allocation, and they have to add up to ۱۰۰٪. When «سختی پذیرش» was added,
   these rules still described seven columns: the new column took the share
   written for the controls, the controls fell to `auto`, and `auto` against a
   budget already fully spent is zero. A zero-width cell does not clip its
   contents, so the three buttons wrapped into a vertical stack and stood
   outside the table entirely.

   The controls are therefore pinned by `:last-child` rather than by an index.
   A column added after this one will make the data widths overflow — which is
   visible and fixable — instead of silently crushing the one cell whose width
   nothing else defends.

   Scoped away from the field view, which is the same class with six columns of
   its own and no business inheriting these. */
.catalog-table:not(.field-table) th:nth-child(1) { width:9%; }
.catalog-table:not(.field-table) th:nth-child(2) { width:17%; }
.catalog-table:not(.field-table) th:nth-child(3) { width:20%; }
.catalog-table:not(.field-table) th:nth-child(4) { width:11%; }
.catalog-table:not(.field-table) th:nth-child(5) { width:11%; }
.catalog-table:not(.field-table) th:nth-child(6) { width:7%; }
.catalog-table:not(.field-table) th:nth-child(7) { width:12%; }
.catalog-table:not(.field-table) th:last-child { width:13%; }
/* Three icon buttons on one line. Wrapping them is what turned a narrow cell
   into a vertical stack, and a stack in a table row reads as a broken row
   rather than as a cell that ran out of room. */
.catalog-row-actions .row-actions { flex-wrap:nowrap; }

/* A menu is as wide as the widest thing in it ------------------------------ */
/* The rows-per-page menu is a narrow field holding three numbers; its list was
   inheriting that width and setting «۲۰۰» in a box built for «۵۰». The start
   edge stays pinned to the field, so in this right-to-left page it grows
   leftward into the room the pagination bar already has. */
.page-size-control .option-panel { inset-inline-end:auto; min-width:max-content; }

@media (max-width:1000px) { .catalog-filters { grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (max-width:600px) { .catalog-filters { grid-template-columns:1fr; } }


/* =======================================================================
   One chevron, one token, one list row
   -----------------------------------------------------------------------
   The rules below finish the shared vocabulary rather than adding to it.
   ======================================================================= */

/* Every chevron sits the same distance inside its box ---------------------- */
/* The shared disclosure marker is placed a quarter of a rem from the edge,
   which is right for a summary that is a line of running text. A boxed control
   has a padding of its own, so the same offset put the glyph hard against the
   border while the words started well inside it. Two selectors, because there
   are two boxed summaries in this product: the block that opens, and the
   picker. */
.disclosure>summary,.multi-select>summary {
  padding-inline:var(--chevron-inset) calc(var(--chevron-inset) * 2 + 1rem);
}
.disclosure>summary::after,.multi-select>summary::after {
  inset-inline-end:var(--chevron-inset);
}

/* A token carries the same two glyphs as everything else ------------------ */
/* The grip used to be «⠿», a braille character drawn by whatever typeface was
   in force, sitting on the text baseline in a box a different size from the
   drawn cross beside it. Both controls are now the same square holding the
   same-sized icon from the product's own sheet, so the two ends of a token
   line up with each other and with its label. */
.selected-pill-list { align-items:stretch; }

/* A list may hold more than it can register --------------------------------- */
/* Exploring the catalogue and building a list are the same gesture here: a
   full list used to refuse the next thing an applicant found, which turned
   «I want to look at this» into «delete something first». The list takes it,
   and says plainly which rows are past the number the official form accepts,
   so the applicant decides what to drop rather than the tool deciding for
   them. The final audit still refuses to lock a list that is over. */
/* A row the applicant added themselves, told apart from one the tool suggested.
   `origin` has been on every choice since migration 009 and nothing showed it:
   «مواردی که کاربر به صورت دستی وارد لیست کرده است، باید از مواردی که به صورت
   سیستم پیشنهاد شده‌اند تفکیک بشوند. مثلا باید یک رنگ متفاوت نمایش داده شوند.»

   A change of surface, the way `.is-over-capacity` below marks its own rows —
   not a coloured rule down the row's start edge, which is the one thing the
   design system forbids by name: no component carries a coloured edge to
   repeat what its own label already says. And this one does have a label. The
   tint is a whisper of the product's blue, because at a hundred and fifty rows
   anything stronger reads as two lists rather than as one list with two kinds
   of row in it.

   Colour is not the only signal either way: the chip where a suggested row
   keeps its تطابق says «افزوده شما» in words, so the distinction survives a
   grayscale screen and a print. The over-capacity state below still wins,
   because being past the official cap is the more urgent of the two facts. */
.track-choice.is-manual { border-color:var(--brand-line); background:#f8fbfe; }
.track-origin {
  display:grid; place-items:center;
  height:var(--action-h); border-radius:9px;
  background:var(--brand-soft); padding-inline:.5rem;
  color:var(--brand-dark); font-size:.76rem; font-weight:700; line-height:1;
  white-space:nowrap;
}
.track-choice.is-over-capacity { border-color:#ecd0ad; background:#fffbf4; }
.track-choice.is-over-capacity .track-rank { background:#fdefe0; color:#8a4a0c; }
.track-choice.is-over-capacity .track-score { background:#fdefe0; color:#8a4a0c; }
.track-over-note {
  display:flex; align-items:flex-start; gap:.5rem;
  margin:0 0 .55rem; border:1px solid #ecd0ad; border-radius:12px;
  background:#fffbf4; padding:.7rem .9rem; color:#7c4a10; font-size:.9rem;
}
.track-over-note[hidden] { display:none; }
.track-over-note strong { flex:none; }
.track-remaining.is-over { color:#8a4a0c; font-weight:700; }

/* Ordering a list by hand -------------------------------------------------- */
/* The rank, the grip and the row's own controls are one strip. A row is full
   of things that are themselves controls, so unlike a token the whole row is
   not the drag surface: the gesture starts on the grip for every pointer. */
/* The grip and the rank belong to the top of the row, not to the middle of it.
   A row whose card wraps to three lines used to centre them against all three,
   which floated the number away from the name it numbers and left the space
   above it as empty as the space below. They sit against the first line and the
   room underneath is simply room. */
.track-choice-lead { display:flex; align-items:center; align-self:start; gap:.3rem; }
.choice-drag-handle {
  display:grid; place-items:center; flex:none;
  width:26px; min-width:26px; height:var(--action-h);
  margin:0; border:0; border-radius:var(--radius-row);
  background:transparent; padding:0; color:#5f6f89;
  cursor:grab; touch-action:none; line-height:0;
  transition:color .16s ease,background-color .16s ease;
}
.choice-drag-handle:active { cursor:grabbing; }
/* A fingertip needs more than a glyph ---------------------------------------
   The grip is the only drag surface, and `touch-action:none` is on it alone:
   that is what lets a finger anywhere else on the row scroll the page, and it
   is why the whole row is not the drag surface. So the grip has to be a target
   rather than an icon. Under a coarse pointer it is 44px wide — the width of a
   fingertip, and about twice the reach it had — while the icon inside stays the
   size it was, because what grew is the reach, not the drawing.

   Width only. The height stays the row's own `--action-h`, so the grip is still
   exactly as tall as the rank beside it: a 44px-tall grip against a 34px rank
   is two controls of two sizes, which is the thing this strip has already been
   corrected for twice. */
.choice-drag-handle .action-icon { width:16px; height:16px; flex:none; }
@media (pointer:coarse) {
  .choice-drag-handle { width:44px; min-width:44px; }
  .choice-drag-handle .action-icon { width:18px; height:18px; }
}
.track-choice:hover .choice-drag-handle,
.track-choice:focus-within .choice-drag-handle { color:#5d7197; }
.choice-drag-handle:hover:not(:disabled) { background:var(--brand-soft); color:var(--brand-dark); }
.choice-drag-handle:disabled { opacity:.35; cursor:not-allowed; }
/* A row in hand looks the same wherever a list of rows is rearranged: the
   choice rows of a plan, the sample on the way in, and the answers of an
   ordered picker are one gesture drawn one way. */
/* A row that has been picked up says so before it has moved.
   On a desktop the lift and the movement arrive together, so the look never
   had to stand on its own. A finger held on a row picks it up while the finger
   is still: for that moment the class is the only thing telling the reader the
   hold worked, and a shadow alone at this size is not a signal, it is a
   suggestion. So the row also takes a ring in the product's own blue — the
   same colour that marks anything else this product considers active.

   Static, and deliberately not animated in. The engine writes `transition:none`
   inline the instant a row goes in hand — so the row cannot lag the pointer
   holding it — which rules out a transition, and the transform belongs to the
   engine too, so a keyframed lift would have to be drawn in the shadow itself.
   That was tried and is the wrong shape for this: for the length of the
   animation the signal would BE the animation, and any frame it does not reach
   is a row that has been picked up and does not look it. The state appears at
   once, whole, and cannot arrive late. Motion on a touch screen comes from the
   row following the finger, and from the tick under it. */
.track-choice.is-dragging,
.selected-pill-list.is-rows .selected-pill.is-dragging {
  border-color:var(--brand); background:#fff;
  box-shadow:0 0 0 3px rgba(27,94,156,.28),var(--shadow-md); opacity:.92;
}
/* A finger resting on a row is picking it up, not selecting the words in it:
   either the selection or the platform's own callout ends the press with a
   `pointercancel` before the hold is finished. Set while the finger is down
   and removed when it lifts, so reading a row with a caret is unaffected. */
.is-holding { -webkit-user-select:none; user-select:none; -webkit-touch-callout:none; }
.track-choices.is-reordering,
.selected-pill-list.is-rows.is-reordering { cursor:grabbing; }

/* Compact: the row is the rank, the field and where it is ------------------ */
/* Compact hides the evidence line so thirty rows fit in a screen, and the
   university was inside it: the list became thirty rows of a field name
   repeated, with nothing to tell them apart. The university follows the field
   on the same line instead, which is how the row is read aloud anyway. */
.offering-at { color:var(--muted); font-size:.88rem; overflow-wrap:anywhere; }
.offering-title .offering-at { display:none; }

/* A thing you can click says what it does ---------------------------------- */
/* Four bare words in a row read as one sentence with no spaces in it. Each
   carries the glyph of what it opens, at the size every other glyph here is. */
.catalog-card-actions .text-link,.catalog-card-actions .text-button { gap:.4rem; }
.catalog-card-actions .action-icon { width:16px; height:16px; flex:none; opacity:.75; }
.catalog-card-actions .text-link:hover .action-icon,
.catalog-card-actions .text-button:hover .action-icon { opacity:1; }
.plan-list .catalog-card-actions { margin-top:.65rem; gap:.15rem; }

/* One picker, one field cell --------------------------------------------- */
/* Every question with a known list of answers is now the same component, so
   the rules it needs are stated once here rather than per page. */
.multi-select-field { min-width:0; }
.multi-select-field>.control-label { margin-bottom:.35rem; }
.essential-grid .multi-select-field>.control-label,
.form-grid .multi-select-field>.control-label { color:#34435e; font-weight:800; }
.how-flow-start .multi-select-field { margin-bottom:1rem; }
.catalog-filters .multi-select-field>.control-label { margin-bottom:.32rem; font-size:.86rem; font-weight:750; }

/* An option may carry an aside ------------------------------------------- */
/* The applicant's own province is offered first in the list of destinations,
   and says why it is first. The aside belongs to the menu only: the chip the
   answer becomes is the province's name and nothing else. */
.option-note { display:block; margin-top:.05rem; color:var(--muted); font-size:.76rem; font-weight:400; line-height:1.6; }
.multi-select-panel .check-label:has(.option-note) { --check-line:26px; }

/* An empty list is still a list ------------------------------------------- */
/* «دعوتی ساخته نشده است.» sat hard against the edge of the box that holds the
   invitations, while every filled row in the same box had a row of padding.
   The three lists on this page now say «nothing yet» the same way, on the same
   surface, with the same room around it. */
.invitation-list>.empty { margin:0; padding:.9rem 1rem; }
.proposal-list>.empty,.comment-list>.empty {
  margin:0; border:1px solid var(--line); border-radius:14px;
  background:var(--surface); padding:.9rem 1rem;
}

/* A printed list says which rows cannot be registered ---------------------- */
/* A draft may hold more than the official form accepts, on purpose, so the
   sheet that is printed and the text that is pasted both have to say so. The
   text stops at the capacity; the sheet keeps the rows and marks them, because
   they are still the applicant's own candidates. */
.export-over-note {
  margin:0 0 .7rem; border:1px solid #ecd0ad; border-radius:10px;
  background:#fffbf4; padding:.6rem .85rem; color:#7c4a10; font-size:.9rem;
}
/* A row the official form will not accept, said in a word as well as a tint:
   a printed sheet is often grayscale, and a warning that is only a colour is
   a warning that does not survive the printer. */
.export-table tr.is-over-capacity { background:#fffbf4; }
.export-over-flag {
  display:inline-block; margin-inline-start:.3rem;
  color:#8a4a0c; font-size:.72rem; font-weight:750; white-space:nowrap;
}

/* One block that opens ---------------------------------------------------- */
/* Three of these sat on one page in three shapes: 14px corners against 24px, a
   white surface against a tinted one, `.panel` borrowed for a surface one of
   them did not need, and three different summary paddings. A reader cannot
   tell what distinguishes two controls that differ in everything, so there is
   one surface, and a page says only which question it is asking. */
.disclosure {
  margin:0 0 1rem;
  border:1px solid var(--line);
  border-radius:var(--radius-surface);
  background:var(--surface);
}
.disclosure>summary {
  display:flex; align-items:center; gap:var(--gap-badge);
  min-height:52px;
  border-radius:var(--radius-surface);
  padding-block:.85rem;
  color:var(--brand-dark); font-weight:800;
  transition:background-color .16s ease;
}
.disclosure>summary:hover { background:var(--brand-soft); }
/* Open, the summary is the head of a box rather than the whole of it. */
.disclosure[open]>summary {
  border-bottom:1px solid var(--line);
  border-end-start-radius:0; border-end-end-radius:0;
}
.disclosure-body { padding:1.1rem var(--chevron-inset); }
.disclosure-body>:last-child { margin-bottom:0; }
/* The badge belongs to the title it qualifies, so it follows the words rather
   than being pushed to the far edge of the row. */
.disclosure-badge {
  flex:none; border-radius:999px; background:var(--brand-soft);
  padding:.24rem .5rem; color:var(--brand-dark); font-size:.76rem; font-weight:700; line-height:1;
}
.disclosure-body>.help-text:first-child { margin-top:0; }




@media (prefers-reduced-motion:reduce) { .disclosure>summary { transition:none; } }

/* An importance card is one column on a phone -----------------------------
   The same accretion as the row of controls above: the two-column rule was
   restated later in this file than the media query that collapses it, so at
   360-680px the description was squeezed into a third of the card while its
   own title wrapped. Restated after it, where it applies. */
@media (max-width:680px) {
  .criteria-form .criterion-card { grid-template-columns:minmax(0,1fr); }
}

/* A table head that stays with its rows -----------------------------------
   The catalogue is read by scrolling: eighty rows of eight columns, and the
   only thing that says which column is which is the row at the top. It scrolled
   away on the first flick, so from the second screen onward every column was a
   value with no name.

   The head is one row of one kind of cell, so its border is one border. It used
   to be a `box-shadow` under each cell, which the sticky-column rule then
   overrode on the first cell alone: seven columns had a line under them and the
   eighth did not. */
.catalog-table thead th {
  position:sticky;
  top:var(--sticky-top);
  z-index:3;
  background:#edf3ff;
  border-block-end:1px solid #cbd7ea;
  color:var(--brand-dark);
  font-weight:800;
  white-space:nowrap;
}
/* The head is the top of the box that holds it, so it carries that box's
   corners. One pixel less than the shell's own radius, because the cells sit
   inside its border and the two curves are not concentric otherwise. */
.catalog-table thead th:first-child { border-start-start-radius:calc(var(--radius-surface) - 1px); }
.catalog-table thead th:last-child { border-start-end-radius:calc(var(--radius-surface) - 1px); }

/* Anything that sticks, sticks flush ---------------------------------------
   A bar pinned to the top of the window is flush with it, so it keeps neither
   the corners it had inside the page nor the gap they implied. One rule, for
   every surface that sticks: `data-sticky` marks it and the shared script
   toggles `is-stuck` from the scroll position. */
[data-sticky].is-stuck { border-radius:0; }
.catalog-table thead.is-stuck th:first-child { border-start-start-radius:0; }
.catalog-table thead.is-stuck th:last-child { border-start-end-radius:0; }
/* Off the page's own surface it needs an edge of its own, and the row beneath
   has to look like it is passing under rather than butting against it. */
.catalog-table thead.is-stuck th { box-shadow:0 6px 14px -10px rgba(23,63,95,.45); }
.step-nav.is-stuck { border-inline-width:0; box-shadow:var(--shadow-sm); }

/* Reading the catalog by field --------------------------------------------
   The same filtered set, counted instead of listed. Six columns rather than
   eight; the offering table's widths are scoped away from this class, so these
   are the only ones that reach it. */
.catalog-view-switch { margin:1.35rem 0 0; }
.field-table th:nth-child(1) { width:34%; }
.field-table th:nth-child(2) { width:12%; }
.field-table th:nth-child(3) { width:12%; }
.field-table th:nth-child(4) { width:11%; }
.field-table th:nth-child(5) { width:13%; }
.field-table th:nth-child(6) { width:18%; }
.field-table td { font-variant-numeric:tabular-nums; }
.field-table td:first-child { font-variant-numeric:normal; }
.field-table tbody tr:hover { background:#eef4ff; }
@media (max-width:820px) {
  .field-table tr { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .field-table td:first-child { grid-column:1/-1; }
}

/* Which exam group a row admits from --------------------------------------
   The first thing that decides whether an offering is a choice at all, and it
   used to be legible only by opening the row and finding it among the source's
   conditions. The colour is a second reading of the word beside it and never
   the only one: the badge always says the group's name, so it is as readable
   without colour as with it, and «همه گروه‌ها» is a real answer rather than a
   missing one. Every ink clears 4.5:1 on its own tint. */
.group-badge {
  flex:none;
  background:#eef1f5; color:#55637a;
}
.group-badge.is-maths { background:#e6eefb; color:#1a4f8a; }
.group-badge.is-science { background:#e2f2ea; color:#1a6a4c; }
.group-badge.is-humanities { background:#fbeee0; color:#8a541a; }
.group-badge.is-art { background:#f1ebfa; color:#5a3f92; }
.group-badge.is-language { background:#e2f0f4; color:#1a5f74; }
.group-badge.is-common,.group-badge.is-other { background:#eef1f5; color:#55637a; }
/* On a row it follows the name it qualifies, on its own line when the name is
   long enough to wrap. */
/* On a row the badges follow the name they qualify, on their own line when the
   name is long enough to wrap.

   **Never a `display` on the cell itself.** A `<td>` given `display:flex` stops
   being a table cell: it leaves the row's shared height and draws at its own
   content height, which is how this cell came to be 48.6px tall in a row of
   90.1px ones. Anything a cell needs laid out is laid out by a box *inside* it
   — here `.group-badges`, which is that box. `tests/landing-design.test.ts`
   asserts no rule in this stylesheet sets `display` on a `td` or `th`. */
.catalog-field-cell .group-badges,.field-table .group-badges { margin-top:.25rem; }

/* Two questions, in the order they depend on each other --------------------
   Which fields an applicant may register is decided by the groups they sat, so
   the second question is asked after the first and offers nothing until it is
   answered. They used to sit side by side, which let a reader choose a field
   their own group cannot register and said so nowhere. */
.guest-steps { display:grid; gap:1.5rem; margin:0 0 1.4rem; padding:0; list-style:none; counter-reset:guest-step; }
.guest-step { min-width:0; }
.guest-step h4 { display:flex; align-items:center; gap:.6rem; margin:0 0 .7rem; font-size:1.05rem; }
.guest-step h4>span {
  display:grid; place-items:center; flex:none; width:1.6rem; height:1.6rem;
  border-radius:50%; background:var(--brand); color:#fff;
  font-size:.82rem; font-weight:800; line-height:1;
}
.guest-step .field-group { margin:0; }
.guest-step .help-text { margin:.45rem 0 0; font-size:.85rem; }
.guest-step .multi-select-field>.control-label { display:none; }
.guest-scope { margin:.7rem 0 0; color:var(--muted); font-size:.9rem; }
.guest-scope strong { color:var(--ink); }

/* How hard the seats are to get.
   One ordinal scale, so it is read as a scale: the tint deepens with the
   competition rather than jumping between unrelated hues, and «برآورد نشده» is
   the flat neutral because it is not a step on the scale at all — it is the
   model declining to place the row. The word is always there, so the colour is
   a second reading and never the only one, and every ink clears 4.5:1 on its
   own tint. Warm, because more competition is the answer that costs the
   applicant something; the product's blue is reserved for what it owns. */
.competition {
  flex:none;
  background:#f3f5f8; color:#55637a;
  /* Lighter than every other chip on the row, and deliberately so.
     A reader reported it plainly: «خیلی توجه می‌گیرند» — in a table of ۵۰ rows
     the difficulty column was the first thing the eye landed on, above the
     field name that is what the row is actually about. It is a supporting
     reading, not the headline, so it stops shouting: the tints below are one
     step paler and the word is set at the weight of ordinary text.

     What does NOT change is the geometry. `.offering-tag`, `.group-badge` and
     `.competition` share one height, radius, padding and type size, and the
     design system is explicit that how big a tag is is not a per-tag decision.
     Weight and tint are exactly what a tag is allowed to vary by, so the whole
     of «کم‌رنگ‌تر و کوچک‌تر» is spent there. */
  font-weight:500;
}
/* One class per band the model actually names. `CompetitionBand` in
   src/modules/difficulty/types.ts is the list — very-high, high, moderate,
   low, insufficient — four steps of one scale plus the one that is not on it.
   Any new band must be given a step here, or it falls through to the base
   chip's flat grey: that is how `significant` and `insufficient` came to be
   uncoloured while the block carried `very-low` and `unknown`, which nothing
   emitted, and the catalogue's «سختی پذیرش» column read as a scale with no
   scale left in it.

   Four steps and not seven, because a reader has to be able to tell them
   apart. «رقابت در بالاترین سطح» sat above «رقابت بسیار زیاد» in words but
   was unreachable by construction, and «رقابت قابل‌توجه» — the vaguest of the
   set — is where the old caps funnelled most of a plan. The tints step far
   enough apart now to be read at a glance rather than compared side by side. */
/* One step paler than they were, and still four steps apart: the tints keep
   the same hues and the same order, so the scale is read exactly as before —
   it simply no longer outshouts the field name beside it. Every ink still
   clears 4.5:1 on its own tint, which is what stops «paler» becoming
   «unreadable». */
.competition-very-high { background:#fbe6e0; color:#7a2418; }
.competition-high { background:#fef2e8; color:#8a3f18; }
.competition-moderate { background:#faf6e9; color:#645425; }
.competition-low { background:#eef3f8; color:#3f5773; }
/* Not a step on the scale: the model declining to place the row. It is the one
   flat, cold chip in the set, so it never reads as «easy». */
.competition-insufficient { background:#f3f5f8; color:#55637a; }
.catalog-difficulty { min-width:104px; }

/* A pager the browser drives, over rows it already has.
   The same shapes as the server's pager, because it is the same control doing
   the same job — a reader should not have to learn two. It reveals itself only
   when the script that drives it is running: without it the table is one long
   list, which is a worse way to read ۶۹۴ rows and not a broken one. */
.client-pager[hidden] { display:none; }
.client-pager { justify-content:space-between; }
.client-pager-status { margin:0; color:var(--muted); font-size:.86rem; font-variant-numeric:tabular-nums; }
.client-pager .pager-icon:disabled { opacity:.35; cursor:not-allowed; }

/* Weights: four levels, four colours, one sentence -------------------------- */
/* The level is the answer to «چقدر مهم است؟», so it carries a colour of its
   own and the reader can take in four factors down a column without reading
   four sentences. The scale is one hue deepening, and the steps are far enough
   apart to be told apart at a glance. The stop in force is written in that
   colour under the track, and the word is always there, so the colour is a
   second reading and never the only one. */
/* The sentence under a factor says what the list will do at the level it is
   set to, so it is the widest thing in the card and reads as prose, not as a
   caption. Where the factor sat there used to be a second line naming what it
   read from — «از تقاضای رشته و جایگاه دانشگاه» — which answered a question
   nobody had asked and pushed the sentence that matters further down. */
.criterion-notes { margin-top:.3rem; }
.importance-note { display:block; color:#3f4d63; font-size:.86rem; line-height:1.75; }
.importance-note[hidden] { display:none; }

/* «رایگان» is the one tag on a row that is unambiguously good news, so it is
   the one tag that is not the neutral grey. Green from the same family the
   exam-group badges already use, rather than a second green invented here. */
.offering-tag.is-free { border-color:#bcd7c6; background:#e2f2ea; color:#1a6a4c; }

/* The choices one audit row covers, folded away until asked for. One problem
   across six choices is one row and one sentence; the six names are the detail
   behind it rather than six repetitions of the sentence. */

/* The disclaimer. It appears wherever a reader might act on what they see, and
   it is the same words every time. Warm rather than red: it is not an error,
   it is the standing condition this whole product operates under. */
.source-disclaimer { margin:1.25rem 0; border:1px solid #e2d3ae; border-radius:14px; background:#fffaf0; padding:.9rem 1.1rem; color:#5c4a22; }
.source-disclaimer p { margin:0; font-size:.88rem; line-height:1.85; }
.source-disclaimer p + p { margin-top:.45rem; }
.source-disclaimer strong { color:#7a5c14; }
.source-disclaimer.is-strong { border-color:#d7bd7f; background:#fff6e3; }
@media print { .source-disclaimer { border-color:#999; background:transparent; } }

/* A picker whose order is part of the answer ------------------------------- */
/* «رشته‌های مورد علاقه» and «کجا حاضرید تحصیل کنید؟» are ranked lists: the
   first entry outranks the last, and that is the whole reason arranging them
   is worth doing. Wrapped into a paragraph of chips, an ordered list looks
   exactly like an unordered one — nothing says which is first, and the drag
   handle only appeared on hover, so there was no standing sign the rows moved
   at all.
   
   One answer per line, numbered, handle always visible. The number is a CSS
   counter, so it follows the DOM through a drag with nothing to keep in sync.
   The chip row stays for the catalog filters, where the answers are a set and
   their order means nothing. */
.selected-pill-list.is-rows { display:grid; gap:.4rem; counter-reset:selected-pill; }
.is-rows .selected-pill { grid-template-columns:auto auto minmax(0,1fr) auto; width:100%; max-width:none; min-height:42px; background:#fff; }
.is-rows .selected-pill::before {
  counter-increment:selected-pill; content:counter(selected-pill, persian);
  display:grid; place-items:center; min-width:26px; height:26px;
  border-radius:7px; background:var(--brand-soft); color:var(--brand-dark);
  font-size:.78rem; font-weight:800; font-variant-numeric:tabular-nums;
}
/* Always visible: a control that appears on hover is a control a reader has to
   already know about. On a touch screen there is no hover at all. */
.is-rows .pill-drag-handle { color:#6b7d9c; cursor:grab; }
.is-rows .pill-drag-handle:active { cursor:grabbing; }
.is-rows .selected-pill-label { font-size:.9rem; }


/* The four questions, and where they open from ----------------------------- */
/* One bar, always in reach, naming every answer the lists are built from and
   the one action that rebuilds them. It replaces a nav of section links, which
   only made sense while the answers were sections — and on a page whose lists
   run to a hundred and fifty rows, «go to the top and come back» was the cost
   of changing one of them. */
.workspace-toolbar {
  position:sticky; top:var(--sticky-top); z-index:6;
  display:flex; align-items:center; flex-wrap:wrap; gap:.45rem;
  margin:0 0 1.75rem; border:1px solid var(--line); border-radius:14px;
  background:var(--surface); padding:.6rem .7rem;
}
.workspace-toolbar-spacer { flex:1 1 auto; }
/* The button is the row that holds its own label and, where it has one, the
   state beside it — so the space between them is this row's gap, not a margin
   the badge carries. `center` is what puts the badge against the middle of the
   label instead of on its baseline; `wrap` is what lets a long name and its
   state stack rather than push each other out of the button. */
.toolbar-button {
  display:inline-flex; align-items:center; justify-content:center;
  flex-wrap:wrap; gap:var(--gap-badge);
  min-height:var(--action-h); border:1px solid var(--field-line); border-radius:10px;
  background:#fff; padding:.35rem .7rem;
  color:var(--brand-dark); font:inherit; font-size:.86rem; font-weight:700; cursor:pointer;
}
.toolbar-button:hover:not(:disabled) { border-color:var(--brand); background:var(--brand-soft); }
.toolbar-button:disabled,.workspace-toolbar .button:disabled { opacity:.45; cursor:not-allowed; }
/* One bar, one height. The button that rebuilds both lists is the filled one
   here, and it says so by being filled rather than by standing 6px taller than
   every control beside it. */
.workspace-toolbar .button { min-height:var(--action-h); }
/* «ناتمام» is the one thing on this bar that is not a name but a state. */
.toolbar-flag { border-radius:6px; background:#fdefe0; padding:.22rem .5rem; color:#8a4a0c; font-size:.74rem; font-weight:800; line-height:1; }

/* A window that holds one question. Same shape as the facts window this page
   already had, because they do the same job. */
.workspace-dialog {
  width:min(720px,calc(100% - 2rem)); max-height:min(86vh,900px);
  border:1px solid var(--line); border-radius:18px; padding:0;
  background:var(--surface); color:var(--ink); box-shadow:var(--shadow-pop);
}
.workspace-dialog::backdrop { background:rgba(23,63,95,.28); }
.workspace-dialog-head {
  display:flex; align-items:start; justify-content:space-between; gap:1rem;
  border-bottom:1px solid var(--line); background:var(--surface);
  padding:1.1rem 1.3rem .9rem; border-radius:18px 18px 0 0;
}
.workspace-dialog-head h2 { margin:0 0 .2rem; font-size:1.35rem; }
.workspace-dialog-head p { margin:0; max-width:52ch; color:var(--muted); font-size:.87rem; line-height:1.75; }
.workspace-dialog-body { padding:1.1rem 1.3rem 1.4rem; }
.workspace-dialog-body .panel { border:0; padding:0; box-shadow:none; }
.workspace-dialog-body .panel + .panel { margin-top:1.2rem; padding-top:1.2rem; border-top:1px solid var(--line); }
@media (max-width:600px) {
  .workspace-dialog { width:100%; max-width:100%; max-height:100%; height:100%; border-radius:0; }
  .workspace-dialog-head { border-radius:0; }
}

/* Weights, in a window ------------------------------------------------------ */
/* Two cards to a row, each holding its name, one line of where it reads from,
   one line of what the list will do at this level, and the dial underneath.
   One card per row made the track the width of the window — a dial with four
   stops does not need six hundred pixels to say which of them it is on, and a
   slider that wide reads as a volume control rather than a four-step choice.
   The notes are one clause each, which is what lets two of these sit side by
   side without turning into towers of two words a line. */
.workspace-dialog .criteria-card-list { grid-template-columns:repeat(2,minmax(0,1fr)); gap:.7rem; }
.workspace-dialog .criterion-card { grid-template-columns:minmax(0,1fr); align-items:start; }
.workspace-dialog .criterion-card .criterion-text { grid-column:1/-1; order:-1; }
.workspace-dialog .criterion-card:not(.is-compact) .importance-control { grid-column:1/-1; }
.workspace-dialog .criterion-card strong { font-size:.98rem; }
.workspace-dialog .criterion-notes { margin-top:.45rem; padding-top:.45rem; border-top:1px dashed var(--line); }
@media (max-width:600px) { .workspace-dialog .criteria-card-list { grid-template-columns:minmax(0,1fr); } }

/* One type scale, with less distance between its ends ----------------------- */
/* The page ran from ۱۳px captions to a ۷۲px heading — a ratio of five and a
   half, which is a poster's range, not a product's. A heading that large is
   not read faster; it is simply the only thing on the screen, and it pushed the
   sentence under it, the toolbar and the first row of every list below the
   fold. The top of the scale comes down to roughly ۳۴px and the floor comes up
   a little, so the whole page holds together at one size and the headings still
   rank what is under them. Sizes are set here, at the end, because the earlier
   layers each set their own and this is what makes the scale one decision. */
h1 { font-size:clamp(1.75rem,3vw,2.15rem); line-height:1.35; letter-spacing:-.02em; margin-bottom:1.1rem; }
h2 { font-size:clamp(1.3rem,2.2vw,1.55rem); line-height:1.45; }
h3 { font-size:1.05rem; line-height:1.55; }
.hero h1 { font-size:clamp(1.7rem,2.4vw,2.1rem); line-height:1.4; }
.message-page h1,.auth-card h1 { font-size:clamp(1.7rem,3vw,2.05rem); line-height:1.4; }
.workspace-head h1 { font-size:clamp(1.65rem,2.6vw,2rem); letter-spacing:-.02em; }
.section-heading h2,.feature-grid>h2,.feature-heading h2,.version-section h2 { font-size:clamp(1.3rem,2.2vw,1.55rem); }
.share-card h1,.share-card h3 { font-size:clamp(1.3rem,3vw,1.7rem); }
.public-share-cta h2,.export-track h2 { font-size:1.3rem; }
.choice-heading h3,.page-dialog h2,.workspace-dialog-head h2 { font-size:1.15rem; }
.app-dialog h2,.facts-head h2,.confirm-dialog h2,.account-form h2 { font-size:1.12rem; }
/* The counters and the ranks were sized like headings and are not headings. */

.choice-rank>strong { font-size:1.4rem; }
.score-ring strong { font-size:1.3rem; }
.lead { font-size:clamp(1rem,1.4vw,1.08rem); }
@media (max-width:760px) { .hero h1 { font-size:clamp(1.55rem,5.5vw,1.9rem); } }
@media (max-width:460px) { .hero h1 { font-size:clamp(1.45rem,6.5vw,1.75rem); } }

/* A button is as wide as what it says --------------------------------------- */
/* «ذخیره مشخصات» sat in a one-column grid, so it stretched the whole width of
   the form: a control the width of the page reads as a banner, and its target
   is nine hundred pixels of nothing on either side of four words. */
.primary-flow-action { justify-self:start; width:max-content; max-width:100%; }

/* The plan list is a list ---------------------------------------------------- */
/* Four plans separated by a hairline read as four paragraphs that happen to
   start with a name. Each is now a row with an edge of its own — the same row
   the favourites page already uses — with the name and what is in it on one
   side and the actions on the other, so the list is scanned by row rather than
   read as prose. */
.empty { color:var(--muted); }
.plan-card:hover { border-color:var(--brand-line); }
.plan-card h2 a,.plan-card h3 a { color:var(--ink); }
.plan-list .row-actions .text-link,.plan-list .row-actions .text-button { font-size:.88rem; }
.plan-submitted-flag {
  width:max-content; margin:0 0 .15rem; border-radius:7px;
  background:var(--brand-soft); padding:.05rem .45rem;
  color:var(--brand-dark); font-size:.8rem; font-weight:800;
}
/* --warning is a fill and icon colour; as small text it falls under 4.5:1, so
   waiting-state sentences take the deep warm ink the required badge wears. */
@media (max-width:760px) {
  .plan-list article { grid-template-columns:minmax(0,1fr); }
  .plan-list .row-actions { grid-column:1; grid-row:auto; margin-top:.35rem; }
}
/* Every row has an edge now, including the first one, which used to drop its
   top border because the rows were separated by rules rather than bounded. */
.plan-list article:first-child { border-top:1px solid var(--line); }

/* Compact rows say where a row is ------------------------------------------- */
/* The compact row hides the evidence line, and the city was in it. Two branches
   of one university teaching one field are then the same line twice; the city
   is what tells them apart, so it follows the university on the title line and
   is shown wherever that line is all there is. */
/* The comma belongs to the university's name, not to a gap: the title line is
   a flex row, so the span pulls back over its own gap to sit against it. */
.offering-city { margin-inline-start:-.35rem; }
.offering-city::before { content:"،"; margin-inline-end:.25rem; }

/* Starting fresh, in development -------------------------------------------- */
/* Not a feature of the product: a way to see the product as somebody who has
   never used it, without editing four tables by hand. It is drawn plainly and
   says what it is, and the route behind it does not exist outside development. */
.dev-reset { margin-bottom:4rem; border-color:#e6c4bf; }
.dev-reset h2 { margin-bottom:.35rem; color:var(--danger); }
.dev-reset p { max-width:62ch; color:var(--muted); font-size:.9rem; }
.dev-reset .button { margin-top:.6rem; }

/* The catalog knows which groups you sat -------------------------------------- */
.catalog-group-note { margin:.9rem 0 0; color:var(--muted); font-size:.9rem; }
.catalog-group-note a { color:var(--brand-dark); font-weight:700; }

/* No rings, no boxes ---------------------------------------------------------
   Every control on this product drew a three-pixel translucent halo when it
   took the focus, and a text box drew a second one as a shadow around itself.
   On a page of stacked fields that halo is the loudest thing on the screen and
   it belongs to whichever control was touched last, which is rarely the thing
   the reader is looking at. The halo also sat outside the control's own box, so
   it pushed against its neighbours and made a settled form look unsettled.

   It is not replaced by nothing. A control under the keyboard now looks the way
   it looks under the pointer, and a little more so: a field darkens the edge
   the text is typed inside, a quiet control fills with the brand tint, a filled
   button deepens, and a link underlines. Nothing is drawn outside a control's
   own edge, so nothing moves and nothing overlaps.

   Where the operating system draws the interface itself — high contrast — the
   real outline comes back, because there the ring is the system's own language
   and these tints are thrown away. */
*:focus { outline:none !important; }
/* …and where a control has no inside to show it in, the fallback this product
   documents: one pixel of --focus at a two-pixel offset, the quietest mark
   that still says «this one». It was written down but never drawn — the strip
   above used to take `:focus-visible` too, at `!important`, so every
   `outline:1px solid var(--focus)` in this file (the catalog scroll region,
   the selection card) was dead, and every control the roster below does not
   name — every button the content engine brings with it, «اشتراک‌گذاری صفحه»
   and «کپی لینک صفحه» and the filter disclosures on /explore, the mobile
   filter button — answered a keyboard with no mark at all.
   `!important` because it still has to outrank site.css's own 2px ring; the
   roster below opts out at the same weight, and later, so it wins. */
*:focus-visible { outline:1px solid var(--focus) !important; outline-offset:2px; }
/* …except where the focused thing is not a control at all.
   <main> and the script's scroll anchors carry `tabindex="-1"` for one reason:
   so focus can be MOVED to them — the skip link, the jump to the
   questionnaire, the status line an error sends the reader to. They are
   destinations, not things to operate, and a hairline drawn around a whole
   content column is noise rather than orientation. site.css says exactly that
   and resets `main:focus`; the universal rule above was outranking that reset
   with its `!important` and drawing the ring back around the entire article —
   which is the stray border a reader sees appear and go (owner, 2026-09-08).
   The one control that carries `tabindex="-1"` is the native <select> that
   `enhanceSelect` hides behind its own trigger, and that trigger is what takes
   the focus and the ring. */
main:focus-visible,
[tabindex="-1"]:focus-visible { outline:none !important; }
:root { --focus-ring:none; }
.select-shell.is-open .select-trigger { box-shadow:none; }

/* Fields: the edge the reader is typing inside. */
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="range"]):focus,
select:focus,
textarea:focus,
.panel input:focus,.panel select:focus,.panel textarea:focus,
.suggest-field>input:focus,
.multi-select-search input:focus,

/* Tick boxes: the row they belong to, which is the target anyway. */
.check-label:has(input:focus-visible) { background:var(--brand-soft); }
input:is([type="checkbox"],[type="radio"]):focus-visible { border-color:var(--brand); }

/* A control that answers focus inside its own box does not also wear the
   fallback hairline: two answers to one question is one too many. */
.button:focus-visible,.icon-button:focus-visible,.toolbar-button:focus-visible,
.button-quiet:focus-visible,.answer-add:focus-visible,
.segmented button:focus-visible,.segmented a:focus-visible,
.pager-icon:focus-visible,.pager-page:focus-visible,.table-sort:focus-visible,
.dialog-close:focus-visible,.select-trigger:focus-visible,
.multi-select>summary:focus-visible,details>summary:focus-visible,
.pill-drag-handle:focus-visible,.selected-pill-remove:focus-visible,.pill-step:focus-visible,
.choice-drag-handle:focus-visible,.step-nav a:focus-visible,.site-header a:focus-visible,
.text-link:focus-visible,.text-button:focus-visible,.back-link:focus-visible,
.skip-link:focus-visible,.catalog-row-actions a:focus-visible,.app-tile:focus-visible,
a:focus-visible,.to-top:focus-visible,
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="range"]):focus-visible,
select:focus-visible,textarea:focus-visible,
input[type="range"]:focus-visible { outline:none !important; }

/* A slider is a thing you drag, and its own track brightens under the thumb.
   A ring around the whole rail says nothing that the rail does not. */

/* Quiet controls fill; the filled one deepens. */
.icon-button:focus-visible,
.toolbar-button:focus-visible,
.button-quiet:focus-visible,
.answer-add:focus-visible,
.segmented button:focus-visible,.segmented a:focus-visible,
.pager-icon:focus-visible,.pager-page:focus-visible,
.table-sort:focus-visible,
.dialog-close:focus-visible,
.select-trigger:focus-visible,
.multi-select>summary:focus-visible,
details>summary:focus-visible,
.pill-drag-handle:focus-visible,.selected-pill-remove:focus-visible,.pill-step:focus-visible,
.choice-drag-handle:focus-visible,
.step-nav a:focus-visible,
.site-header a:focus-visible {
  border-color:var(--brand);
  background:var(--brand-soft);
  color:var(--brand-dark);
}
.button:focus-visible { background:var(--brand-dark); }
/* The quiet variant is quiet under focus too: the filled rule above would
   pour the deep fill under its dark label and swallow the words. */
.button.button-quiet:focus-visible { border-color:var(--brand); background:var(--brand-soft); color:var(--brand-dark); }
.selected-pill-remove:focus-visible { color:var(--danger); }
/* A link has no edge to darken, so it takes a surface — but the hover tint is
   a wash of nothing against this paper, so focus takes the deeper tint and a
   hairline drawn inside the link's own edge. Still inside the box, still no
   halo, and legible even on the tint the current nav link already wears.
   Not an underline: Persian descenders cross it, which is why nothing on this
   product underlines a link in the first place. */
.text-link:focus-visible,.text-button:focus-visible,.back-link:focus-visible,
.skip-link:focus-visible,.catalog-row-actions a:focus-visible,
a:focus-visible { border-radius:7px; background:var(--brand-tint); color:var(--brand-dark); box-shadow:inset 0 0 0 2px var(--brand); }
/* Said again at the specificity the header's aria-current rule and the strip
   links' own fills carry, so the focused state always wins the background. */
.site-header .brand:focus-visible,.site-header .main-nav>a:focus-visible,
.step-nav a:focus-visible,.footer-links a:focus-visible {
  background:var(--brand-tint); color:var(--brand-dark);
  box-shadow:inset 0 0 0 2px var(--brand);
}
/* The slider has no edge of its own; the track it runs on brightens. */
.importance-control input[type="range"]:focus-visible { background:var(--brand-soft); }
.catalog-table-shell:focus-visible { background:transparent; }
/* The stops the roster above misses. The scroll regions have no fill to
   change, so the fallback hairline returns for them — declared !important
   only because the global strip above is. The card answers on the label, so
   a checked card and a focused checked card are not the same picture. */
.to-top:focus-visible { border-color:var(--brand); background:var(--brand-soft); }

.selection-card:has(input:focus-visible) { border-color:var(--brand); background:var(--brand-soft); outline:1px solid var(--focus); outline-offset:2px; }
@media (forced-colors:active) {
  *:focus-visible { outline:2px solid Highlight !important; outline-offset:2px; }
}

/* An answer is not something you open a menu to see -------------------------
   The picker used to be a closed box saying «۳ مورد انتخاب شده». To read the
   three you opened it; to change their order you closed it again; and the
   thing the reader had actually come to do — see what they had said and move it
   — was two clicks away behind a control that looked like it asked a question
   it had already been answered.

   So the answers are the component and the menu is a button under them. The
   rows are the same rows the choice lists use, with the same grip and the same
   two arrows, because moving something up one place is the same act in both
   and a reader should learn it once. `+ افزودن` opens a search under the last
   row, and each thing found joins the end of the list.

   The catalog filters keep the closed menu: eight of them share one grid, and
   a filter that grows when it is answered pushes the other seven down the page
   every time. That is the one place the box is the right shape. */
.ordered-multi-select.is-open-picker { gap:.55rem; }
/* What the picker says while it holds nothing. For an optional question this
   is not «nothing chosen» but the answer that leaving it empty already gives. */
.picker-empty { margin:0; color:var(--muted); font-size:.88rem; }
.picker-empty[hidden] { display:none; }

.answer-adder { display:grid; gap:.5rem; justify-items:start; }
.answer-add {
  display:inline-flex; align-items:center; gap:.4rem;
  min-height:var(--action-h); border:1px dashed var(--brand-line);
  border-radius:var(--radius-row); background:var(--surface); padding:.35rem .85rem;
  color:var(--brand-dark); font:inherit; font-size:.88rem; font-weight:700; cursor:pointer;
  transition:border-color .16s ease,background-color .16s ease;
}
.answer-add .action-icon { width:15px; height:15px; flex:none; }
.answer-add:hover:not(:disabled),.is-adding .answer-add {
  border-style:solid; border-color:var(--brand); background:var(--brand-soft);
}
.answer-add:disabled { opacity:.5; cursor:not-allowed; }
/* The search sits under the button that opened it, at the width of the answers
   above it, so nothing floats over the rest of the form. */
.answer-panel {
  width:100%; border:1px solid var(--brand-line); border-radius:var(--radius-control);
  background:var(--surface); padding:.4rem;
}
.answer-panel[hidden] { display:none; }
/* The bar is pulled out to the panel's edges so it spans the full width while
   it is stuck, and that overhang used to paint a square corner over the
   panel's own rounded top. It carries the panel's radius itself now — one less
   than the panel's, because it sits inside that border. */
.answer-panel .multi-select-search {
  position:sticky; top:-.4rem; z-index:1;
  margin:-.4rem -.4rem .35rem !important; padding:.4rem !important; background:var(--surface);
  /* `inherit`, not the panel's own token: the panel takes a different radius
     inside a dialog than it does on the page, and a hardcoded number matched
     one of the two. The bar is pulled out to the panel's inner edge, so the
     radius it needs is exactly the panel's. */
  border-start-start-radius:inherit;
  border-start-end-radius:inherit;
}
.answer-panel [data-multi-options] { display:grid; max-height:16rem; overflow-x:hidden; overflow-y:auto; }
.answer-panel .check-label { --check-line:26px; margin:0; padding:.4rem .45rem; font-weight:500; }
.answer-panel .check-label span { min-width:0; overflow-wrap:anywhere; }
.answer-panel .option-note { display:block; color:var(--muted); font-size:.8rem; }
[data-multi-options]>.check-label.is-selected-option { display:none; }
.answer-panel .multi-select-empty { margin:.45rem; }
@media (prefers-reduced-motion:reduce) { .answer-add { transition:none; } }

/* A row of answers carries its own two arrows -------------------------------
   Dragging is the quick way to move a row and it is also the way that asks the
   most of a hand, a screen and a browser. The arrows are the plain way, they
   are the same arrows the choice rows use, and the one that reached the end of
   its travel goes quiet rather than disappearing — a control that vanishes
   takes the reader's place with it. */
.is-rows .selected-pill { grid-template-columns:auto auto minmax(0,1fr) auto auto auto; gap:.15rem; padding:4px 6px; }
/* The count sat two pixels from the edge of the row. */
.is-rows .selected-pill::before { margin-inline-end:.15rem; }
/* The grip leads, then the number — the order a choice row already uses. A
   counter drawn by CSS is always the first child of its box, so the grip is
   moved in front of it here rather than the number being moved behind it: the
   number has to stay a counter, which is what keeps it right through a drag
   with nothing to re-number. */
.is-rows .pill-drag-handle { order:-1; }
.pill-step {
  display:grid; place-items:center; width:26px; min-width:26px; height:26px;
  margin:0; border:0; border-radius:7px; background:transparent; padding:0;
  color:#6b7d9c; font:inherit; line-height:0; cursor:pointer;
  transition:color .16s ease,background-color .16s ease;
}
.pill-step .action-icon { width:16px; height:16px; flex:none; }
.selected-pill:hover .pill-step,.selected-pill:focus-within .pill-step { color:#5d7197; }
.pill-step:hover:not(:disabled) { background:var(--brand-soft); color:var(--brand-dark); }
.pill-step:disabled { opacity:.3; cursor:not-allowed; }
@media (pointer:coarse) { .pill-step { width:36px; min-width:36px; height:36px; color:#5d7197; } }
@media (prefers-reduced-motion:reduce) { .pill-step { transition:none; } }

/* The scale is under the track, not beside it -------------------------------
   A single word beside the slider named the stop the thumb was on and never
   named the other three, so the only way to find out what the ends of the
   scale were called was to drag to them and back. All four stops are written
   under the track, at the position the thumb takes for each, and the one in
   force is the one in ink. The word is still on the control itself, as
   `aria-valuetext`, so it is never read from position alone. */
.criteria-form .importance-control,
.criterion-card .importance-control { display:grid; gap:.15rem; margin:0; border:0; padding:0; }
.criterion-card .importance-control input[type="range"] {
  width:100%; min-height:24px; margin:0; padding:0; border:0; border-radius:6px;
  background:transparent; accent-color:var(--level); cursor:pointer;
}
.criterion-card .importance-control input[type="range"]:disabled { cursor:not-allowed; opacity:.55; }
/* Inset by half a thumb at each end, so the first and last stops sit under the
   two positions the thumb can actually reach. */
.importance-scale { position:relative; height:1.25rem; margin-inline:9px; font-size:.78rem; line-height:1.25rem; }
.importance-scale span { position:absolute; top:0; width:0; text-align:center; white-space:nowrap; color:var(--muted); }
.importance-scale span::before {
  content:""; position:absolute; top:-.45rem; inset-inline-start:0;
  width:1px; height:.3rem; background:var(--field-line);
}
.importance-scale [data-importance-tick="0"] { inset-inline-start:0; width:auto; text-align:start; }
.importance-scale [data-importance-tick="1"] { inset-inline-start:33.333%; }
.importance-scale [data-importance-tick="2"] { inset-inline-start:66.667%; }
.importance-scale [data-importance-tick="3"] { inset-inline-end:0; width:auto; text-align:end; }
.importance-scale [data-importance-tick="3"]::before { inset-inline-start:auto; inset-inline-end:0; }
.criterion-card[data-importance="0"] [data-importance-tick="0"],
.criterion-card[data-importance="1"] [data-importance-tick="1"],
.criterion-card[data-importance="2"] [data-importance-tick="2"],
.criterion-card[data-importance="3"] [data-importance-tick="3"] { color:var(--level); font-weight:800; }
.criterion-card[data-importance="0"] [data-importance-tick="0"]::before,
.criterion-card[data-importance="1"] [data-importance-tick="1"]::before,
.criterion-card[data-importance="2"] [data-importance-tick="2"]::before,
.criterion-card[data-importance="3"] [data-importance-tick="3"]::before { background:var(--level); width:2px; }

/* A weight, beside the question it reads --------------------------------------
   «چقدر محل تحصیل مهم است؟» is a question about the answer given directly
   above it, and it used to be asked two windows away. The copy that sits under
   its own question is drawn as a continuation of that question rather than as
   a card of its own: a rule, a quieter name, the same dial. The four sentences
   stay in the window where the four factors are compared, which is the only
   place they answer anything. */
.criterion-card.is-compact {
  display:grid; grid-template-columns:minmax(0,1fr); gap:.5rem;
  margin-top:.85rem; border:0; border-top:1px solid var(--line); border-radius:0;
  background:transparent; padding:.85rem 0 0;
}
/* The dial is the same dial in every window, so it is the same width, the same
   height and the same scale in every window. It used to be squeezed into a
   narrow second column beside its own name, which read as a different, smaller
   control asking a smaller question, and crowded its four stops into half the
   room they have in «اهمیت معیارها». The name sits above it now, as it does
   there, and the track stops at the width two of these cards side by side give
   it rather than running the whole width of a window — a four-stop dial six
   hundred pixels long reads as a volume control. Only the name is quieter
   here, because the question it belongs to is written directly above it. */
.criterion-card .importance-control { max-width:18.5rem; }
.criterion-card.is-compact strong { color:var(--muted); font-size:.88rem; font-weight:700; }

/* One question under another --------------------------------------------------
   «محدوده پیشنهادها» asked its two questions in two columns inside a titled
   box inside a disclosure inside the form: three frames around two fields, and
   the columns halved the width of the very lists the questions are answered in.
   One column, one rule between them, and the frames are gone. */
.question-stack { display:grid; gap:0; }
.question-block { display:grid; gap:.35rem; }
.question-block + .question-block { margin-top:1.5rem; padding-top:1.5rem; border-top:1px solid var(--line); }

/* A window's opening sentence is as wide as the window ------------------------
   It was held to fifty-two characters inside a seven-hundred-pixel window, so
   a two-line sentence broke into four and left half the head empty. */
/* A paragraph is as wide as the box it is in ---------------------------------
   Sentences were capped at fifty-two characters inside a seven-hundred-pixel
   window, at five hundred pixels under a section heading, at sixty-two
   characters in a panel — three different measures, each set where the rule was
   written rather than where the reader is. A two-line sentence broke into four
   and left the rest of its box empty. The container decides the measure; the
   paragraph fills it. */
.workspace-dialog-head p,
.section-heading>p,
.dev-reset p,
.panel p,
.lead { max-width:none; }

/* A window scrolls inside itself ---------------------------------------------
   The scrollbar was drawn by the dialog element, so it ran the full height of
   the window and squared off the rounded corner it passed through — the box
   read as clipped rather than as a card. The dialog is a column now: a head
   that does not move, and a body that scrolls. The radius clips the bar at both
   ends, and the head no longer has to be sticky to stay put.
   `[open]` matters: a bare `display` on `<dialog>` overrides the `display:none`
   the browser uses to keep a closed one out of the page. */
.workspace-dialog[open] { display:flex; flex-direction:column; overflow:hidden; }
.workspace-dialog-head { position:static; flex:0 0 auto; }
.workspace-dialog-body { flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain; }

/* Where you live is one field ------------------------------------------------
   The province was a sentence under the box — «استان تهران» — which reads as a
   second thing to answer and takes a line to say one word. It is not a
   question: it is the answer this field has already given, read off the city.
   So it sits inside the field, at the far end, in the quieter ink a derived
   value deserves.
   The suggestion list is anchored to the whole field rather than to the input
   inside it, so the list of cities is as wide as the box it drops from. */
.home-place { max-width:26rem; }
.place-field {
  position:relative; display:flex; align-items:center; gap:.5rem;
  min-height:var(--control-h); border:1px solid var(--field-line);
  border-radius:var(--radius-control); background:var(--surface); padding-inline:.8rem;
}
.place-field .suggest-field,
.place-field .suggest-field:has(.suggest-panel:not([hidden])) { position:static; flex:1 1 auto; min-width:0; }
.place-field:has(.suggest-panel:not([hidden])) { z-index:60; }
.place-field:hover { border-color:var(--field-line-hover); }
.place-field:focus-within { border-color:var(--brand); }
.place-field input {
  width:100%; min-height:calc(var(--control-h) - 2px) !important;
  margin:0 !important; border:0 !important; border-radius:0;
  background:transparent; padding:0 !important;
}
.place-province {
  flex:none; max-width:45%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  border-radius:7px; background:var(--brand-soft); padding:.1rem .45rem;
  color:var(--brand-dark); font-size:.82rem; font-weight:700;
}
.place-province:empty { display:none; }

/* Taking an answer back ------------------------------------------------------
   One control, drawn once, on every field that offers it. The browser's own
   search X is turned off rather than styled: it exists on some browsers and
   not others, it is absent on touch where it would help most, and it is drawn
   in the operating system's shape at the operating system's size — three
   reasons it can never be the control this product means. `window.actionIcon`
   draws the same glyph every other × in the page is drawn with.

   It stands in the quietest ink available and answers a hover, because it is a
   shortcut and not a thing to be noticed while typing. It is out of the tab
   order: a keyboard clears a field by selecting and deleting, and a stop
   between every field and the next one costs more than it gives. */
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration { -webkit-appearance:none; appearance:none; }
.clearable-field { position:relative; display:block; min-width:0; }
.clearable-field>input { width:100%; padding-inline-end:2.4rem !important; }
.field-clear {
  display:grid; place-items:center; flex:none;
  width:28px; height:28px; border:0; border-radius:8px;
  background:transparent; padding:0; color:var(--muted);
  line-height:0; cursor:pointer;
  transition:color .16s ease, background-color .16s ease;
}
.field-clear[hidden] { display:none; }
.field-clear .action-icon { width:15px; height:15px; }
.field-clear:hover { background:var(--brand-soft); color:var(--brand-dark); }
.clearable-field>.field-clear {
  position:absolute; inset-inline-end:.4rem; top:50%; transform:translateY(-50%);
}
@media (pointer:coarse) { .field-clear { width:36px; height:36px; } }
.home-place small { display:block; margin-top:.35rem; color:var(--muted); font-size:.82rem; font-weight:400; line-height:1.7; }

/* The profile, at the size of what it asks ------------------------------------
   Two headed panels and a sentence between them framed three short answers.
   They share one row now, and the results sit against the group they belong to
   instead of in a block of their own. Nothing was removed: the window asks
   exactly what the profile page asks, because it is the profile page. */
.account-essentials .account-facts { grid-template-columns:repeat(2,minmax(0,1fr)); gap:.7rem 1rem; align-items:start; }
.account-essentials .account-facts>.home-place { grid-column:1/-1; }
.account-essentials .field-group { margin:1.1rem 0 0; }
.account-essentials .exam-groups { gap:.25rem; }
.account-essentials .exam-group-results { grid-template-columns:repeat(3,minmax(0,1fr)); gap:.4rem .6rem; margin:.3rem 0 .15rem; }
.account-essentials .exam-group-results label:not(.check-label) { font-size:.8rem; }
/* A rank is four or five digits. The box was a third of the form wide and set
   the digits at the same size as the caption above them, so the largest thing
   on the row was the empty space beside the number. It is sized to what it
   holds now, and the number inside it is the biggest type in the field —
   tabular, because three of these stand in a column and a rank read against
   another rank is a comparison. */
.account-essentials .exam-group-results input {
  min-height:38px !important; padding:.3rem .45rem !important;
  font-size:1.02rem; font-variant-numeric:tabular-nums;
}
/* The group and its three results on one line, where there is a line to put
   them on. They are answers *about* the group, not a second question under it,
   and stacking them turned four ticked groups into a page of boxes. It wraps
   rather than squeezing, so the narrow dialog this same partial renders in
   falls back to the stack without being told. */
@media (min-width:760px) {
  .account-essentials .exam-group { display:flex; align-items:center; flex-wrap:wrap; gap:.2rem 1rem; }
  .account-essentials .exam-group>.check-label { flex:0 0 auto; min-width:12rem; }
  .account-essentials .exam-group-results {
    flex:1 1 20rem; grid-template-columns:repeat(3,minmax(0,8.5rem));
    justify-content:start; margin:0;
  }
}
/* The one act of a long form, kept on screen -------------------------------
   `position:sticky` with `bottom`, so the bar rides the foot of the window
   while the form is taller than it and settles into the page at the form's own
   end. Nothing is added to the layout and nothing is covered permanently: the
   sentence beside the button is what the act is for, so a reader meeting the
   bar halfway down the form is not asked to guess. */
.form-actions {
  position:sticky; bottom:0; z-index:8;
  display:flex; align-items:center; flex-wrap:wrap; gap:.5rem 1rem;
  margin-top:1.2rem;
  border-top:1px solid var(--line);
  background:var(--paper); padding:.8rem 0;
}
.form-actions-note { color:var(--muted); font-size:.88rem; }
@media (max-width:560px) { .form-actions-note { display:none; } }

/* Where the task goes after the form ---------------------------------------
   One heading, one sentence, one button, in the reading flow right after the
   text — the shape this product's closing calls to action already have. While
   the answers are incomplete the button is still a link to somewhere real, so
   it is not disabled; it is quiet, and the sentence above it says why. */
.next-step { margin-top:1.5rem; }
.next-step h2 { margin-bottom:.35rem; font-size:1.3rem; }
.next-step p { margin-bottom:1rem; color:var(--muted); max-width:52ch; }
.next-step[data-incomplete] .button {
  border-color:var(--brand-line); background:var(--surface); color:var(--brand-dark);
}
.workspace-dialog .account-essentials h2 { margin-bottom:.45rem; }
@media (max-width:560px) {
  .account-essentials .account-facts { grid-template-columns:minmax(0,1fr); }
  .account-essentials .exam-group-results { grid-template-columns:repeat(2,minmax(0,1fr)); }
}

/* The lists are behind the answers ------------------------------------------
   Saving and rebuilding are two acts now, so there is a state between them,
   and a state nobody can see is a state that reads as a bug: the applicant
   changes an answer, the page comes back, and the same hundred and fifty rows
   are still there. The line says which way round it is — the answer is the new
   thing, the list is the old one — and it is brand-coloured rather than amber,
   because nothing is wrong. It is simply not done yet.

   The button that finishes it carries a mark rather than a ring: a filled dot
   before its label, which is the same «there is something here» a badge makes
   everywhere else. A halo around it would be the focus ring this product has
   just stopped drawing, in a place where it would mean something else. */
.workspace-toolbar.is-behind { border-color:var(--brand-line); }
/* The state is a thing beside the button, not a mark on it -------------------
   A dot in front of a label reads as decoration on the control it sits in, and
   at half a rem of white on brand it was the quietest thing on the bar while
   naming the one condition the reader needs to act on. It is now its own
   element with its own glyph and its own words, and the button is left to say
   only what pressing it does. */
.rebuild-flag {
  display:inline-flex; align-items:center; gap:.4rem;
  border:1px solid #e2c98f; border-radius:999px;
  background:#fff7e8; padding:.28rem .6rem .28rem .5rem;
  color:#7a5c14; font-size:.83rem; font-weight:800; white-space:nowrap;
}
.rebuild-flag svg { flex:none; animation:rebuild-breathe 2.4s ease-in-out infinite; }
@keyframes rebuild-breathe {
  0%,100% { opacity:.55; transform:rotate(0deg); }
  50%     { opacity:1;   transform:rotate(-24deg); }
}
/* The button keeps a quiet mark of its own so the pair still reads as one
   state when the flag has wrapped to a line of its own. */
@media (prefers-reduced-motion:reduce) {
  .rebuild-flag svg { animation:none; opacity:1; }
}
@media (max-width:520px) { .rebuild-flag span { display:none; } }
.lists-behind {
  display:grid; gap:.15rem;
  margin:-1.1rem 0 1.6rem; border:1px solid var(--brand-line); border-radius:14px;
  background:var(--brand-soft); padding:.75rem 1rem; color:var(--brand-dark);
}
.lists-behind strong { font-size:.95rem; }
.lists-behind span { color:#3c5573; font-size:.87rem; line-height:1.85; }

/* A status line with nothing to say takes no room ---------------------------
   Every form on this product opens with a live region that is empty until
   something is saved. It kept its two-rem height while empty, so each window
   began with a band of nothing between its heading and its first question. */
.save-status:empty { min-height:0; margin-block-end:0; }

/* One corner, on everything that is a surface -------------------------------
   The toolbar was ۱۴px, the panels ۱۶, a track panel something else again, and
   a window ۱۸ — four radii on four boxes stacked down one page, which reads as
   four kinds of box rather than one kind at four sizes. `--radius-surface` was
   already declared and already meant this; it is now what every top-level
   surface uses. The window comes down with them, which is also what stops its
   scrollbar running into a corner too round for it. */
.panel,
.workspace-dialog,
.workspace-toolbar,
.track-panel,
.locked-banner,
.lists-behind,
.export-warning,
.source-disclaimer,
.plan-list article,
.account-prompt,
.catalog-demo-notice,
.field-control .answer-panel,
.step-nav { border-radius:var(--radius-surface); }
@media (max-width:600px) { .workspace-dialog { border-radius:0; } }

/* How full a list is ---------------------------------------------------------
   One bar, and the whole of it is the list: the part inside the registrable
   capacity, then the part past it. The colour changes exactly at the capacity,
   so there is no tick to explain and no second scale to read — and the room
   left is the empty end of the bar, which is the one thing a number alone
   («۱۳۸ از ۱۵۰») makes you do arithmetic for.

   Both lines above it say the same numbers in words, so this is a second
   reading and never the only one. It is `aria-hidden` for that reason.

   The two fills are ΔE ۲۹ apart to a normal eye and ΔE ۲۳ to a protan one, with
   a two-pixel gap of the rail between them, so the capacity line survives both
   colour blindness and a printer. */
/* The gap belongs between the two fills, not after the last of them: a third
   segment holding the room left would otherwise be preceded by a notch of rail
   that reads as part of the bar. */
.capacity-meter {
  display:flex; align-items:stretch;
  width:100%; max-width:22rem; height:7px; margin-top:.6rem;
  border-radius:999px; background:#e7ecf3; overflow:hidden;
}
.capacity-fill { min-width:0; border-radius:999px; background:var(--brand); }
.capacity-over { min-width:0; margin-inline-start:2px; border-radius:999px; background:var(--warning); }
.capacity-rest { min-width:0; }
@media (forced-colors:active) {
  .capacity-fill { background:Highlight; }
  .capacity-over { background:CanvasText; }
}

/* Where you are, on a page a hundred and fifty rows long ---------------------
   Three links on the bar that is already stuck to the top. A second sticky
   strip under it would take a phone's viewport down to nothing between them. */
.workspace-toolbar-rule { width:1px; height:20px; background:var(--line); }
.workspace-sections { display:flex; align-items:center; gap:.15rem; }
.workspace-sections a {
  min-height:34px; display:inline-flex; align-items:center;
  border-radius:9px; padding:.3rem .55rem;
  color:var(--muted); font-size:.84rem; font-weight:700; text-decoration:none;
}
.workspace-sections a:hover { background:var(--brand-soft); color:var(--brand-dark); }
.workspace-sections a[aria-current] { background:var(--brand-soft); color:var(--brand-dark); }
@media (max-width:760px) { .workspace-toolbar-rule { display:none; } }
/* On touch these grow with everything else: the density switch and the strip
   links were the smallest targets on pages that are otherwise uniformly 44px. */
@media (pointer:coarse) {
  .segmented button,.segmented a,.workspace-sections a,
  .page-tools>a,.page-tools>button,.page-tools>form>button { min-height:44px; }
}

/* A plan is renamed where its name is read ---------------------------------- */
.plan-title { display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; }
.title-rename { flex:none; border-color:transparent; background:transparent; color:var(--muted); }
.plan-title:hover .title-rename,.title-rename:focus-visible { border-color:var(--brand-line); color:var(--brand-dark); }

/* Every chip on a row is the same shape --------------------------------------
   «رقابت بسیار زیاد» and «ریاضی و فنی» are built by different partials and each
   carried its own padding, radius and type size, so a strip of chips stepped up
   and down as it was read. What a chip *says* is carried by its ink and its
   fill; how big it is is not a per-chip decision.

   Stated against the three chip classes themselves rather than against
   `.offering-tags > *`. That positional form reached the *wrapper* spans as
   well — `.offering-tag-group` got the chip's padding and border, and then the
   `.group-badge` inside it got them again. The exam-group chip therefore sat in
   a box one padding larger than itself: it measured 24.8px against every other
   chip's 29.6px, and the space before it measured 12.8px against every other
   gap's 4.8px. Selecting the thing you mean is also what keeps this working
   wherever a chip is drawn — inside the strip, inside a table cell, or as a
   direct child of the row in the compact reading. */
.offering-tag,
.group-badge,
.competition {
  min-height:24px; display:inline-flex; align-items:center;
  /* A transparent edge on the ones that have no edge of their own, so a chip
     with a border and a chip without are still the same height. */
  border:1px solid transparent; border-radius:7px; padding:.1rem .45rem;
  font-size:.78rem; font-weight:700; line-height:1.6;
  white-space:nowrap;
}
/* The two wrappers around the exam-group chips carry no appearance of their
   own: they exist so the server can name the group in one place and the
   compact reading can hide it by name.

   Inside a chip strip they are taken out of the layout entirely, so the badges
   are laid out by whichever box is laying out the rest of the strip and take
   that box's gap — which is what makes every gap between adjacent chips the
   same one. Without it a row admitting four groups drew them with no gap at
   all, because they were inline children of an inline wrapper rather than flex
   items of the strip.

   **Only inside a chip strip.** Applied everywhere, `display:contents` leaves
   the badges as bare inline items of whatever holds them, and the catalogue's
   «رشته» cell then had to become a flex box to space them — which is what took
   that `<td>` out of the table's own layout and left it 48.6px tall in a row of
   90.1px cells. Elsewhere the wrapper stays a box and brings its own gap. */
.offering-tags .offering-tag-group,
.offering-tags .group-badges { display:contents; }
.group-badges {
  display:inline-flex; flex-wrap:wrap; gap:var(--chip-gap);
  vertical-align:middle;
}
/* A row the booklet leaves ungrouped has nothing to put in the wrapper. With
   `display:contents` an empty wrapper already draws nothing, but the rule is
   kept explicit so a future `gap` on it cannot start costing a slot. */
.offering-tag-group:empty { display:none; }

/* Where an offering is, on the line that names it ---------------------------
   The field, then the university that teaches it, then the city and province it
   is in — one line, three inks, each fact once. The city and province are the
   quietest of the three because they are the answer you scan for last and the
   one most often repeated down a column. */
.offering-place { color:var(--brand-dark); font-size:.88rem; font-weight:600; }
.offering-where { color:var(--muted); font-size:.85rem; }
.offering-where[hidden] { display:none; }

/* A saved answer is a sentence, not a stripe --------------------------------- */
.save-status {
  min-height:0; width:auto; max-width:none;
  margin-block:.35rem 1.1rem; border:1px solid var(--brand-line); border-radius:12px;
  background:var(--brand-soft); padding:.7rem .95rem;
  color:var(--brand-dark); font-size:.9rem; line-height:1.85;
}
.save-status:empty { display:none; }
.save-status.form-error { border-color:#e6c4bf; background:#fdf1ef; color:var(--danger); }

/* The lists are behind the answers — warm, because nothing is wrong ----------
   Blue said «this is a fact about the product»; warm says «this is waiting for
   you», which is what it is. Same family as the source disclaimer, which is the
   other standing condition this product asks a reader to hold in mind. */
.lists-behind { border-color:#e2d3ae; background:#fffaf0; color:#5c4a22; }
.lists-behind strong { color:#7a5c14; }
.lists-behind span { color:#6a5730; }
.workspace-toolbar.is-behind { border-color:#e2d3ae; }

/* Room around things that were touching -------------------------------------
   A button sitting on the top edge of the list it acts on, and a warning
   pressed against the bottom of the rows it warns about, both read as part of
   what they touch rather than as the separate thing they are. */
.new-plan-action { margin-bottom:1.25rem; }
.trash-note { margin:0 0 1.25rem; }

/* The title line says all three, in both densities -------------------------
   The university used to be hidden on the title line and shown only in compact
   reading, because the line underneath repeated it. That line holds only the
   branch now, so hiding the university left a row saying «مهندسی کامپیوتر —
   تبریز» and never naming the university that teaches it. Both densities show
   the same identity; compact drops the branch and the tags, which is what makes
   it compact. */
.offering-title .offering-at { display:inline; }
.offering-city { margin-inline-start:0; }
.offering-city::before { content:none; }
/* Each part of the line is a step quieter than the one before it, so the eye
   runs field → university → place without reading a separator. */
/* A rule, not a character. The em-dash that used to sit here is not printed
   anywhere in this product, and «·» is ruled out for the same reason one line
   up in `offering_place`: a separator between two facts should not read as
   punctuation belonging to either of them. A hairline is a divider and nothing
   else, and it holds the line's three parts apart at a fifth of an em-dash's
   weight. */
.offering-at::before,.offering-place::before {
  content:""; display:inline-block; vertical-align:-.06em;
  width:1px; height:.85em; margin-inline:.2rem .45rem;
  background:#cdd7e6;
}
@media (forced-colors:active) {
  .offering-at::before,.offering-place::before { background:CanvasText; }
}

/* Where an applicant lands ---------------------------------------------------
   Six destinations, each a link with a number on it. The tile is the link, so
   nothing inside it is a second control competing with it, and the number is
   the only thing here the destination itself cannot say sooner.

   Three columns on a desk, two on a tablet, one on a phone: at one column the
   tiles are rows, which is what a list of places should be at that width. */
.app-tiles {
  display:grid; grid-template-columns:repeat(auto-fit,minmax(15.5rem,1fr));
  gap:.7rem; margin:0 0 1.1rem;
}
.app-tile {
  display:flex; align-items:flex-start; gap:.7rem;
  border:1px solid var(--line); border-radius:var(--radius-surface);
  background:var(--surface); padding:.95rem 1rem;
  color:inherit; text-decoration:none; cursor:pointer;
}
.app-tile:hover { border-color:var(--brand); background:var(--brand-soft); }
/* The whole tile is the control, so it shows focus the way every other control
   here does: inside its own box, by taking the surface it takes on hover. */
.app-tile:focus-visible { border-color:var(--brand); background:var(--brand-soft); outline:none; }
.app-tile-icon {
  flex:none; display:inline-flex; align-items:center; justify-content:center;
  width:32px; height:32px; border-radius:10px;
  background:var(--brand-soft); color:var(--brand-dark);
}
.app-tile:hover .app-tile-icon,.app-tile:focus-visible .app-tile-icon { background:#fff; }
.app-tile-icon svg { width:18px; height:18px; }
/* The name of a section and the one value that qualifies it belong on the
   same line: as two full-width rows of a grid they read as two headings,
   and cost a row of the card each. The sentence under them keeps its own
   line by claiming the full basis. */
.app-tile-text { display:flex; flex-wrap:wrap; align-items:baseline; gap:.1rem var(--gap-badge); min-width:0; flex:1 1 auto; }
.app-tile-lead { flex:1 1 100%; }
.app-tile-text strong { color:var(--ink); font-size:.98rem; }
.app-tile-lead { color:var(--muted); font-size:.82rem; line-height:1.7; }
/* The number sits under the title it belongs to. It used to sit at the far end
   of the tile, where a reader had to carry the label across the row to it. */
.app-tile-count {
  display:flex; align-items:baseline; gap:.3rem; flex-wrap:wrap;
  color:var(--brand-dark); font-size:1.05rem; font-weight:800; line-height:1.4;
}
.app-tile-count small { color:var(--muted); font-size:.76rem; font-weight:700; }
/* Which way this tile goes. A shortcut that looks like a card needs to say it
   is a link, and a chevron says it without a second control in the box. */
.app-tile-go { flex:none; color:var(--muted); margin-top:.2rem; }
.app-tile:hover .app-tile-go,.app-tile:focus-visible .app-tile-go { color:var(--brand-dark); }
/* An answer that is not finished is the one state here that blocks the others,
   so it borrows the same warm ink the workspace uses for it. */
.app-tile.is-waiting { border-color:#e2c98f; background:#fffaf0; }
.app-tile.is-waiting .app-tile-icon { background:#fdefe0; color:#8a4a0c; }
.app-tile.is-waiting .app-tile-count { color:#8a4a0c; }

/* The heading over a set of shortcuts, and the quiet line under them --------
   A label, not a section title: what follows is secondary to the panel above
   it, and a heading at panel size would say otherwise. */
.section-label {
  margin:0 0 .55rem; color:var(--muted);
  font-size:.82rem; font-weight:800;
}
.app-aside {
  display:flex; align-items:center; flex-wrap:wrap; justify-content:space-between;
  gap:.4rem 1rem; margin:1.4rem 0 1.5rem; padding-top:1rem;
  border-top:1px solid var(--line); color:var(--muted); font-size:.86rem;
}
.app-aside form { display:flex; margin:0; }
.app-aside .text-button { color:var(--muted); text-decoration:none; }
.app-aside .text-button:hover { color:var(--danger); background:none; text-decoration:underline; text-underline-offset:.2em; }
.app-aside .text-link { color:var(--muted); }

/* What this account is for ---------------------------------------------------
   The one thing on the front page that is not a link to somewhere else: the
   plan last worked on, the step it is on, and the act that moves it. It is the
   emphasised surface, so it is the only one, and it earns that with a stronger
   hairline and more room rather than with a fill or a shadow. */
.resume-panel {
  border:1px solid var(--brand-line); border-radius:var(--radius-surface);
  background:var(--surface); padding:1.35rem 1.4rem 1.2rem; margin-bottom:1.6rem;
}
.resume-head { display:flex; align-items:baseline; justify-content:space-between; flex-wrap:wrap; gap:.3rem 1rem; }
.resume-panel h2 { margin:0; font-size:clamp(1.25rem,2.2vw,1.6rem); line-height:1.5; }
.resume-panel h2 a { text-decoration:none; }
.resume-panel h2 a:hover { color:var(--brand); }
/* One fact each, divided by a hairline. The rule is a divider and not
   punctuation, which is why it is drawn rather than typed: this product writes
   no «·» in running text and no dash anywhere. */
.resume-meta { display:flex; align-items:center; flex-wrap:wrap; gap:.1rem 0; margin:0; color:var(--muted); font-size:.86rem; }
.resume-meta span+span::before {
  content:""; display:inline-block; vertical-align:-.06em;
  width:1px; height:.85em; margin-inline:.55rem; background:var(--brand-line);
}
/* The steps and what they are waiting for are one block, set apart from the
   plan's own facts above them. */
.resume-stage { margin-top:1rem; border-top:1px solid var(--line); padding-top:.35rem; }
.resume-stage .stage-steps { margin-top:.75rem; }
.resume-lead { margin:.85rem 0 0; color:var(--ink); }
.resume-note { color:#8a4a0c; font-weight:700; }
.resume-actions { display:flex; align-items:center; flex-wrap:wrap; gap:.5rem .9rem; margin-top:1rem; }
.resume-actions .button { min-height:var(--control-h); }
/* The quiet way out of a panel stands the same height as the button beside it:
   two controls on one line are one line. */
.resume-actions .text-link { min-height:var(--control-h); }
.resume-panel.is-first-run { border-color:var(--line); }

/* The five steps ------------------------------------------------------------
   A list of named steps, not a bar: a bar says how much is left and never says
   of what. Done is filled, the current one is outlined and named in the ink of
   the page, and what is not started yet is quiet. The state is also written
   out for a screen reader, because a fill is a colour telling the whole story. */
.stage-steps {
  list-style:none; display:flex; flex-wrap:wrap; align-items:center; gap:.3rem 0;
  margin:1rem 0 0; padding:0;
}
.stage-step { display:flex; align-items:center; gap:.4rem; min-width:0; }
/* A number inside a circle is centred on the text, not on the line box the
   text sits in. Vazirmatn reserves room above every glyph for the marks Arabic
   script puts there, so a line box centred in the circle hangs its digits low;
   `line-height:1` takes that reserve out of the box. The same line is why the
   chips and badges beside it were sitting low in their pills. */
.stage-step-mark {
  flex:none; display:inline-flex; align-items:center; justify-content:center;
  width:22px; height:22px; border-radius:999px;
  border:1px solid var(--brand-line); background:var(--surface);
  color:var(--muted); font-size:.76rem; font-weight:800; line-height:1;
  /* Half the shift each way, because a fixed-height box centres what is left
     of it after its own padding. */
  padding-top:calc(var(--numeral-shift) * 2);
}
.stage-step-label { color:var(--muted); font-size:.82rem; white-space:nowrap; }
/* The hairline between two steps is a divider, not a track: it says the two are
   in sequence and nothing about how far along the sequence anybody is. */
.stage-step:not(:last-child)::after {
  content:""; flex:none; width:1.1rem; height:1px;
  background:var(--line); margin-inline:.45rem;
}
.stage-step.is-done .stage-step-mark { border-color:var(--brand); background:var(--brand); color:#fff; }
.stage-step.is-done .stage-step-label { color:var(--brand-dark); }
.stage-step.is-current .stage-step-mark { border-color:var(--brand); border-width:2px; color:var(--brand-dark); }
.stage-step.is-current .stage-step-label { color:var(--ink); font-weight:800; }

/* The same five steps on one line, for a row in a list. */
.stage-mark { display:inline-flex; align-items:center; gap:.45rem; color:var(--muted); font-size:.84rem; }
.stage-mark-dots { display:inline-flex; gap:.14rem; }
.stage-mark-dots i { width:6px; height:6px; border-radius:999px; background:var(--brand-line); }
.stage-mark-dots i.is-done { background:var(--brand); }
.stage-mark.is-complete { color:var(--success); font-weight:700; }

/* Where a plan starts -------------------------------------------------------
   The same four windows the toolbar opens, read as a sequence instead of as a
   row of equal buttons. A number, what the question is, what it is for, and
   the one control that answers it — and the build at the end, so «and then
   this» is on the page rather than in the reader's head. It exists only while
   there is no list yet; from the moment there is one, the lists are the page.

   The rows use the same marks the plan stage does, at the size a row of text
   wants rather than the size a strip of five wants, so a reader who has seen
   «مرحله ۲ از ۵» on the account page meets the same shapes here. */
.plan-start { margin-bottom:1.5rem; }
.plan-start h2 { margin-bottom:.3rem; font-size:1.25rem; }
.plan-start-lead { margin-bottom:1rem; color:var(--muted); max-width:56ch; }
.plan-start-steps { list-style:none; display:grid; gap:.4rem; margin:0 0 1.1rem; padding:0; }
.plan-start-step {
  display:flex; align-items:center; flex-wrap:wrap; gap:.5rem .8rem;
  border:1px solid var(--line); border-radius:var(--radius-row);
  background:var(--surface); padding:.6rem .8rem;
}
.plan-start-step.is-current { border-color:var(--brand); background:var(--brand-soft); }
.plan-start-mark {
  flex:none; display:inline-flex; align-items:center; justify-content:center;
  width:26px; height:26px; border-radius:999px;
  border:1px solid var(--brand-line); background:var(--surface);
  color:var(--muted); font-size:.82rem; font-weight:800; line-height:1;
  padding-top:calc(var(--numeral-shift) * 2);
}
.plan-start-mark .action-icon { width:14px; height:14px; padding:0; }
.plan-start-step.is-done .plan-start-mark {
  border-color:var(--success); background:var(--success); color:#fff; padding-top:0;
}
.plan-start-step.is-current .plan-start-mark { border-color:var(--brand); border-width:2px; color:var(--brand-dark); }
.plan-start-text { flex:1 1 14rem; display:grid; gap:.1rem; min-width:0; }
.plan-start-text strong { display:flex; align-items:center; gap:.4rem; flex-wrap:wrap; }
.plan-start-text>span { color:var(--muted); font-size:.85rem; }
.plan-start-step .button { flex:none; }
.plan-start-build { width:max-content; }

/* A page that says its own state ------------------------------------------ */
.page-state { margin:0 0 1.2rem; color:var(--muted); font-size:.88rem; }
.page-state.is-waiting { color:#8a4a0c; }
.page-state strong { color:inherit; }

/* One plan, one row ---------------------------------------------------------
   The same shape wherever a plan is listed: what it is called, where it has
   got to, and what can be done to it. The first row of the account's own list
   is the plan being carried on with, so it also says what that plan is waiting
   for and offers the act in a filled button. Nothing else is emphasised. */
.plan-list { display:grid; gap:.6rem; }
.plan-card {
  display:flex; align-items:flex-start; gap:.4rem;
  border:1px solid var(--line); border-radius:var(--radius-surface);
  background:var(--surface); padding:.9rem 1.05rem .9rem .8rem;
}
.plan-card .plan-drag-handle { margin-top:.1rem; margin-inline-start:-.2rem; }
.plan-card-body { flex:1 1 auto; min-width:0; }
.plan-card:hover .plan-drag-handle,
.plan-card:focus-within .plan-drag-handle { color:#5d7197; }
.plan-card.is-dragging {
  border-color:var(--brand); background:#fff;
  box-shadow:var(--shadow-md); opacity:.94;
}
.plan-list.is-reordering { cursor:grabbing; }
.plan-row-head { display:flex; align-items:baseline; flex-wrap:wrap; gap:.2rem .7rem; }
.plan-card h2,.plan-card h3 { margin:0; font-size:1.05rem; }
.plan-card h2 a,.plan-card h3 a { text-decoration:none; }
.plan-card h2 a:hover,.plan-card h3 a:hover { color:var(--brand); }
/* One line of facts, divided by a hairline: the same treatment the panel above
   used, so a plan reads the same way whichever list it is in. */
.plan-row-meta {
  display:flex; align-items:center; flex-wrap:wrap; gap:.1rem 0;
  margin:.35rem 0 0; color:var(--muted); font-size:.86rem;
}
.plan-row-meta>*+*::before {
  content:""; display:inline-block; vertical-align:-.06em;
  width:1px; height:.85em; margin-inline:.55rem; background:var(--brand-line);
}
.plan-row-note { margin:.15rem 0 0; color:#8a4a0c; font-size:.84rem; font-weight:700; }
.plan-list .row-actions { margin-top:.7rem; }
.text-link.is-primary-row-action,.text-button.is-primary-row-action { color:var(--brand-dark); font-weight:800; }

/* The head of the list: what it is, how full it is, and the one act that adds
   to it. It was a panel head inside a panel inside a grid. */
.plan-list-head {
  display:flex; align-items:center; justify-content:space-between;
  flex-wrap:wrap; gap:.6rem 1rem; margin:0 0 .8rem;
}
/* The heading is the row that holds its own name and the count that
   qualifies it, so the space between them is this row's gap. */
.plan-list-head h2 { display:flex; align-items:center; flex-wrap:wrap; gap:var(--gap-badge); margin:0; font-size:1.15rem; }

/* How many plans there is room for -------------------------------------------
   Beside the heading it qualifies, and again as a sentence when the answer is
   «none», with the one action that changes it. */
.plan-capacity {
  border-radius:999px;
  background:var(--brand-soft); padding:.28rem .5rem;
  color:var(--brand-dark); font-size:.82rem; font-weight:800; line-height:1;
}
.plan-room-note {
  display:grid; gap:.15rem;
  margin:0 0 1.1rem; border:1px solid #e2d3ae; border-radius:12px;
  background:#fffaf0; padding:.7rem .95rem; color:#5c4a22; font-size:.88rem; line-height:1.85;
}
.plan-room-note strong { color:#7a5c14; }
/* The same sentence, said before it is a refusal. «Here is the rule» and «you
   have met the rule» are not the same message, and only the second one is a
   warning, so only the second one is drawn as one. */
.plan-room-note.is-quiet {
  border:0; background:none; padding:.15rem 0; color:var(--muted); font-size:.84rem;
}

/* An empty state answers for the whole grid, not for one cell of it: a panel
   holding «you have nothing here» half the width of the page reads as the
   first of two things rather than as the answer. */
.review-plan-grid>.empty,.review-plan-grid>.empty-state { grid-column:1 / -1; }

/* The pages inside the account -----------------------------------------------
   They open on their own content rather than on a strip of tabs naming their
   neighbours, so the head can be shorter: the reader arrived here on purpose
   and the first useful line should be near the top of the window. */
.account-head { padding-block:2.1rem 1.1rem; }
.account-head+.page-state { margin-top:-.3rem; }

/* On a phone the five steps are two lines of names. The hairline between two
   of them says «and then», which is a lie at the end of a wrapped line, so at
   this width the steps stand apart on their own. */
@media (max-width:560px) {
  .stage-steps { gap:.4rem .8rem; }
  .stage-step:not(:last-child)::after { display:none; }
  .resume-head { flex-direction:column; align-items:flex-start; }
  .resume-panel { padding:1.1rem 1rem; }
  .resume-actions .button { flex:1 1 100%; }
}

/* A reviewer reads the plan the way its owner does -------------------------
   The review row was a flex box with the rank drawn by a CSS counter on the
   first `strong` it could find, which is fine for a list nobody rearranges and
   wrong the moment one is. It is now the same three-part row the workspace
   uses: the grip and the rank on one side, the card in the middle, the
   controls on the other. The counter is gone because the rank is now an
   element that the ordering keeps in step. */
.review-choice-list { display:grid; gap:.5rem; margin:0; padding:0; list-style:none; counter-reset:none; }
.review-choice-list li {
  display:grid; grid-template-columns:auto minmax(0,1fr) auto;
  align-items:start; justify-content:stretch; gap:.75rem;
  border:1px solid var(--line); border-radius:14px; background:#fff; padding:.7rem .8rem;
}
.review-choice-list li strong::before { content:none; }
.review-choice-list li div:first-child { display:block; }
.review-choice-list li>div:last-child { display:flex; align-items:center; gap:.3rem; }
.review-choice:hover,.review-choice:focus-within { border-color:#a9bde8; background:#fbfcff; }
.review-choice.is-dragging { border-color:var(--brand); background:var(--brand-soft); opacity:.9; }
.review-owner-note { margin:.4rem 0 0; color:var(--ink); font-size:.88rem; line-height:1.85; }
.review-choice-comments { margin:.3rem 0 0; color:var(--brand-dark); font-size:.82rem; font-weight:700; }
.review-choice-list li.track-empty {
  display:block; border-style:dashed; color:var(--muted); text-align:center;
}

/* The answers the list was built from --------------------------------------
   A reviewer asked whether an order is right, without being shown the question
   it answers, can only guess. «چرا هیچ گزینه‌ای خارج از تهران نیست؟» is
   answered here and nowhere else on the page. */
.review-settings { display:grid; gap:1rem; }
.review-setting-list { display:grid; gap:.7rem; margin:0; }
.review-setting-list>div {
  display:grid; grid-template-columns:11rem minmax(0,1fr); gap:.5rem 1rem;
  border-bottom:1px solid var(--line); padding-bottom:.7rem;
}
.review-setting-list>div:last-child { border-bottom:0; padding-bottom:0; }
.review-setting-list dt { color:var(--muted); font-size:.88rem; font-weight:700; }
.review-setting-list dd { margin:0; color:var(--ink); font-size:.92rem; line-height:1.9; }
.review-weights { display:flex; flex-wrap:wrap; gap:.35rem; }
.review-weight {
  display:inline-flex; align-items:center; gap:.35rem;
  border:1px solid var(--brand-line); border-radius:999px;
  background:var(--brand-soft); padding:.15rem .6rem;
  color:var(--brand-dark); font-size:.83rem;
}
.review-weight b { font-weight:800; }
.review-settings-actions { display:grid; gap:.3rem; justify-items:start; }
.review-settings-actions .help-text { margin:0; }
@media (max-width:600px) { .review-setting-list>div { grid-template-columns:1fr; } }

/* A suggestion is read as what it changes ---------------------------------- */
.suggestion-diff { display:grid; gap:.5rem; margin:.6rem 0 0; }
/* A changed row is marked by its own ink and by the fact that it shows a
   second line at all. Nothing here carries a coloured edge repeating what the
   label beside it already says. */
.suggestion-diff>div {
  display:grid; grid-template-columns:10rem minmax(0,1fr); gap:.3rem .9rem;
}
.suggestion-diff dt { color:var(--muted); font-size:.85rem; font-weight:700; }
.suggestion-diff>div.is-changed dt { color:var(--brand-dark); font-weight:800; }
.suggestion-diff dd { display:grid; gap:.15rem; margin:0; font-size:.9rem; line-height:1.85; }
/* What it says now, then what it would say. The first is struck through only
   where the two differ, which the container already knows. */
.suggestion-before { color:var(--muted); }
.is-changed .suggestion-before { text-decoration:line-through; text-decoration-thickness:1px; }
.suggestion-after { color:var(--ink); font-weight:700; }
.suggestion-diff>div:not(.is-changed) .suggestion-after { display:none; }
@media (max-width:600px) { .suggestion-diff>div { grid-template-columns:1fr; } }

/* One comment box, aimed from the row it is about -------------------------- */
.comment-thread { display:grid; gap:1rem; }
.comment-form { display:grid; gap:.5rem; }
.comment-target {
  display:flex; align-items:center; flex-wrap:wrap; gap:.5rem;
  margin:0; color:var(--brand-dark); font-size:.9rem; font-weight:700;
}
.comment-target .text-button { font-size:.85rem; font-weight:700; }
.suggest-weights { display:grid; gap:.6rem; }
.suggest-weight { display:grid; grid-template-columns:minmax(0,1fr) 11rem; align-items:center; gap:.75rem; }
@media (max-width:600px) { .suggest-weight { grid-template-columns:1fr; } }

/* Browsing every matching offering is a job, so it gets a window ------------
   It used to open in place: the filters, twenty rows and a pager pushed the
   list they add to a screen and a half down the page, and the button that
   commits a selection sat wherever the block happened to end. The window is a
   column — head, a body that scrolls, and a foot stuck to its bottom edge — so
   the count and the button are in reach from anywhere in the list. */
.track-browse {
  display:flex; align-items:center; flex-wrap:wrap; gap:.5rem .8rem;
  margin:0 0 1rem;
}
.track-browse .help-text { margin:0; flex:1 1 18rem; }
.matches-dialog { width:min(860px,calc(100% - 2rem)); }
.matches-dialog .workspace-dialog-head { flex:none; }
.matches-dialog .workspace-dialog-body {
  flex:1 1 auto; min-height:0; overflow-y:auto;
  /* The foot is sticky inside this box, so the padding that would sit under it
     belongs to the foot rather than to the box. */
  padding-bottom:0;
}
.matches-dialog .matches-foot {
  position:sticky; bottom:0; z-index:1;
  display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap;
  gap:.6rem 1rem; margin:0; border-top:1px solid var(--line);
  background:var(--surface); padding:.85rem 0 1.1rem;
}
.matches-dialog .matches-pager { justify-content:center; margin-top:1rem; }
.matches-dialog .matches-selection { gap:.75rem; }
/* Room between the end of the list and the bar stuck to the bottom of it.
   The body's own bottom padding was moved onto the foot (so the foot's ground
   reaches the window's edge), which left the last row of a scrolled list
   butting straight against the foot's hairline — and, when the list was short
   enough not to scroll, the pager sitting against it too. The gap belongs to
   what scrolls, so it is stated on the last thing above the foot rather than
   restored to the box. */
.matches-dialog .matches-pager { margin-bottom:1.1rem; }
.matches-dialog .match-groups:last-child { margin-bottom:1.1rem; }

/* One field per block, so a window of five fields is five lines --------------
   A hundred rows of a field nobody is looking at is a hundred rows to scroll
   past. Each group opens on its own; the first is open because a window that
   opens onto nothing but headings looks empty. */
/* Nothing here re-draws the block that opens.
   This selector used to carry its own surface, its own summary padding, its
   own chevron (drawn as a second `::after`, at a different size, on a different
   inset, with its own reduced-motion rule) and its own pill for the count — so
   a window full of `.disclosure` elements held accordions that looked like
   nothing else in the product. The class is `disclosure match-group`, and only
   the second half of that belongs here: what a field's block does that a block
   in general does not.

   `--chevron-inset` is the shared summary padding, so the rows line up under
   the words that name them instead of starting at the box's edge. */
.match-group { margin:0; }
.match-group>.disclosure-body { padding:.5rem var(--chevron-inset) .7rem; }
/* The count is a fact about the block, not a state worth a badge: a pill in
   brand ink on every heading made «۱۲ گزینه» compete with the field's own
   name. It is the quietest thing on the line and sits right after the name it
   counts, which is where a reader looks for it. */
.match-group-count { color:var(--muted); font-size:.82rem; font-weight:600; }
/* Pushed to the far end of the line that opens the block, which is where the
   reader asked for it. It is a control inside a control, so it stops the press
   from reaching the summary — see `renderGroups` in decision-workspace.js. */
.match-group-all { margin-inline-start:auto; flex:none; }
.match-group>summary { flex-wrap:wrap; }

/* Handing a filtered catalog on ---------------------------------------------
   Two quiet tools on the page head, in the page-tools strip every workspace
   uses: copy the address, or the browser's own share sheet where one exists.
   Only what is specific to sharing is said here. */
.catalog-share .action-icon { width:15px; height:15px; flex:none; }
.catalog-share .save-status { margin:0; padding-inline:.45rem; font-size:.82rem; }
.catalog-share .save-status:empty { display:none; }
/* Only drawn where the clipboard was refused, which is the one case a reader
   has to finish by hand. */
.catalog-share-fallback {
  flex:1 1 14rem; min-height:32px;
  border:1px solid var(--brand-line); border-radius:9px;
  background:#fff; padding:.25rem .5rem; font:inherit; font-size:.82rem;
}

/* The way in ----------------------------------------------------------------
   One promise beside the product itself. The copy column says what the reader
   gets and offers the one act their state calls for; the sample beside it is
   the real list component, alive, at the size a plan draws it. */
/* Top-aligned: the copy's first line and the sample list's head start on the
   same line, so the two columns read as one deliberate row rather than the
   shorter one drifting to the middle of the taller. */
.hero { align-items:start; }
@media (min-width:1021px) {
  .hero { grid-template-columns:minmax(0,5fr) minmax(0,6.5fr); }
  .hero-copy { grid-column:1; grid-row:1; }
  .hero-preview { grid-column:2; grid-row:1; }
  .hero-scale { grid-row:2; }
}
.hero-copy { min-width:0; max-width:40rem; }
/* Same pill as the preview panel's «نمونه نمایشی» tag — one badge shape for
   the whole page, said once at the top rather than repeated everywhere. */
/* «۱۰۰٪ رایگان» in the colour the product already uses for «this went well».
   It was the brand blue, which is the colour of everything else on the page —
   the buttons, the badges, the name — so the one claim a visitor most wants to
   believe was the same weight as the furniture around it. Green is the one
   thing on this screen it cannot be confused with. */
.hero-badge {
  display:inline-flex; align-items:center; gap:.4rem; margin:0 0 1.1rem; border-radius:999px;
  background:#e2f2ea; padding:.45rem 1rem;
  color:#14634a; font-size:.95rem; font-weight:800; line-height:1;
}
.hero-badge .action-icon { color:#1a7a5a; }
.hero-badge .action-icon { width:18px; height:18px; }
.hero-copy .lead { max-width:52ch; color:#46536b; line-height:2; }
.hero-copy .actions { margin-top:1.6rem; gap:.7rem 1rem; }
/* A trust signal reads faster as a number with a name under it than as a
   clause inside a sentence — the same three facts, in the shape a reader
   actually scans for before committing an evening to a tool. */
/* How big the thing behind the button is, said at the size of the claim it is
   (owner, 2026-09-07). These were two small pairs at .8rem tucked under the
   buttons — the size a page uses for a caveat, for the one fact a first-time
   visitor most needs: a national catalogue, searchable, in one place. Each is
   a figure and its noun spelled out in full on its own bordered surface. No
   shadow and no gradient (the design system reserves shadow for what floats),
   and no «۱۰۰٪ رایگان» tile, because the badge at the top of the hero is
   already where that is said once. */
.hero-scale { grid-column:1/-1; min-width:0; }
.hero-stats {
  display:grid; grid-template-columns:repeat(auto-fit,minmax(10rem,1fr));
  gap:.6rem; list-style:none; margin:0; padding:0;
}
.hero-stats li {
  display:flex; flex-direction:column; gap:.2rem; min-width:0;
  border:1px solid var(--line); border-radius:var(--radius-surface);
  background:var(--surface); padding:.9rem 1.05rem;
}
.hero-stats strong {
  color:var(--brand-dark); font-size:1.9rem; font-weight:850; line-height:1.15;
  font-variant-numeric:tabular-nums;
}
.hero-stats span { color:var(--muted); font-size:.85rem; line-height:1.6; }
.hero-stats-source { margin:.7rem 0 0; color:var(--muted); font-size:.8rem; }
/* The answer to «and then what?», next to the thing being pressed. It used to
   be the questionnaire's own lead two screens below, which is not where the
   decision is made (owner, 2026-09-07). Green, because it is the same kind of
   fact as «۱۰۰٪ رایگان» above it and the reader should recognise it as one. */
.hero-assurance {
  display:flex; align-items:flex-start; gap:.45rem;
  margin:.95rem 0 0; color:#14634a; font-size:.88rem; font-weight:650; line-height:1.8;
}
.hero-assurance .action-icon { flex:none; width:16px; height:16px; margin-top:.3em; color:#1a7a5a; }
.hero-assurance>span { min-width:0; }
.hero-scope { margin:1.2rem 0 0; color:#46536b; font-size:.88rem; line-height:1.9; }
.hero-scope strong { color:var(--ink); font-weight:800; }
.hero-scope+.hero-scope { margin-top:.25rem; }

/* The claim, and the four things it rests on -------------------------------
   A five-clause paragraph said exactly this and asked to be read; a visitor
   deciding whether to spend an evening here is scanning, not reading. Four
   lines, one act each, in the order they happen. The marker is a rule rather
   than a bullet glyph: a disc in front of a Persian line sits at the wrong
   height and reads as punctuation the sentence did not ask for. */
.hero-points {
  list-style:none; display:grid; gap:.55rem;
  margin:1.4rem 0 0; padding:0;
  max-width:46ch;
}
/* Each point is something the reader gets, so it is ticked rather than
   bulleted (owner, 2026-09-08) — the same drawn check and the same green as
   the «۱۰۰٪ رایگان» badge above, which is where this page already says «yes,
   included». The glyph is the sprite's, sized in whole pixels and aligned to
   the first line's own text rather than to the middle of a block that may run
   to two. `currentColor` carries it through forced-colors on its own, so the
   old override for the dot is gone with the dot. */
.hero-points li {
  display:flex; gap:.55rem;
  color:#46536b; font-size:.98rem; line-height:1.9;
}
.hero-point-icon { flex:none; width:17px; height:17px; margin-top:.5em; color:#1a7a5a; }
.hero-points li>span { min-width:0; }

/* The sample list ------------------------------------------------------------
   The one act this product is for, working, before anybody signs in — so it has
   to read as one ranking component rather than as five rows of loose labels.

   It was the latter. The row was a wrapping flex line holding the name, the
   university, the city, the match and the exam group in sequence, so every row
   broke wherever its own text ran out of room: measured at 1440px the five rows
   were 45/45/64/45/64 tall and the group badge started at x=254, 336, 605, 363
   and 585. Nothing a reader compares down a column was in a column.

   It is a grid now:

     [ grip + rank ] [ name · exam group / university ] [ up/down ]

   Everything between the row and the leaves is `display:contents`, so the
   leaves are placed directly by that grid and nothing in between can introduce
   a box of its own. The container query is on the section rather than the
   viewport because this column is half a hero on a wide screen and the whole
   page on a phone — its width has never been the window's width.

   Each row sizes its own middle; the rows were **subgrids of the list** for one
   revision, so that a column was a column down all five. That is the right
   answer for a row of many facts and the wrong one for a row of two: sized to
   the widest name in the list, «حقوق» sat in a cell wide enough for «زبان و
   ادبیات انگلیسی» and its own exam group stood three centimetres away from it.
   The owner asked for the group to be attached to the field (2026-09-07), and
   attached beats aligned here. The lead and the controls are fixed widths, so
   those still line up down the list without any help. */
.hero-preview {
  min-width:0; align-self:center;
  container-type:inline-size; container-name:preview;
}
/* The head belongs to the list under it: the title sits on the same start edge
   as the rows, and «نمونه نمایشی» is a quiet aside on that line rather than a
   second thing competing with it from the far end. */
.hero-preview-head {
  display:flex; align-items:baseline; flex-wrap:wrap; gap:.15rem .5rem;
  margin:0 0 .55rem;
}
.hero-preview-head p { margin:0; color:var(--ink); font-size:1rem; font-weight:800; }
/* Said on the box: these five could not be one person's list, because they
   span all five exam groups. It is a demonstration, not a claim — so it is
   marked, quietly, and never at the weight of the title it follows. */
.hero-preview-tag {
  flex:none; border-radius:999px; background:var(--brand-soft); padding:.15rem .5rem;
  color:var(--brand-dark); font-size:.7rem; font-weight:700; line-height:1.6;
}

/* One grid for the whole list, so a column is a column down all five rows.
   The identity and the metadata are sized to their own widest content and sit
   next to each other; the slack in the row goes into a spacer *after* them, so
   the controls still finish the row without the match and the group being
   pushed to its far end. Stretching the identity to `1fr` put half a row of
   empty space between an offering's name and what is said about it, which is
   the fastest way to make two related things read as two unrelated ones. */
.hero-preview-list { list-style:none; margin:0; padding:0; display:grid; gap:.35rem; }
.hero-preview-list .track-choice {
  display:grid; grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center; gap:0 .6rem;
  padding:.5rem .6rem;
}
.hero-preview-list .offering-title>strong { grid-column:2; grid-row:1; }
.hero-preview-list .offering-meta { grid-column:2; grid-row:2; }
.hero-preview-list .offering-at { grid-column:2; grid-row:3; }
.hero-preview-list .track-choice-lead { grid-row:1/span 3; }
.hero-preview-list .demo-row-actions { grid-column:3; grid-row:1/span 3; }
/* The wrappers between the row and its leaves carry no appearance of their
   own here, so the row's own grid places the leaves. */
.hero-preview-list .track-choice-body,
.hero-preview-list .offering-body { display:contents; }

/* The rank and the grip are one thing: the two halves of «move this row». */
.hero-preview-list .track-choice-lead {
  grid-column:1; display:flex; align-items:center; gap:.2rem;
}
.hero-preview-list .track-rank {
  width:2rem; min-width:2rem; height:2rem;
  font-size:.82rem;
}
/* Height only, again: 1.6rem here against 26px there was the same width
   written twice at a higher specificity, which is all it took for the
   fingertip-sized grip to stop at the plan and never reach the sample. */
.hero-preview-list .choice-drag-handle { height:2rem; }
.hero-preview-list .choice-drag-handle .action-icon { width:14px; height:14px; }

/* What the row is, and where. Two lines that never trade places: the name owns
   the first, the university and the city share the second. */
/* Two rows, declared, rather than one wrapping line: the name owns the first
   and the university and the city share the second, and they cannot trade
   places whatever their lengths. As a wrapping flex line the university had to
   be allowed to *grow* to keep the city off a third row, and growing is what
   pushed the city to the far end of the block — half a centimetre of nothing
   between two halves of one answer. Grid tracks size to their content and pack
   to the start, so the city sits against the university and the leftover width
   stays where it belongs, outside the group. */
.hero-preview-list .offering-title {
  grid-column:2; min-width:0;
  display:grid; grid-template-columns:minmax(0,auto) auto;
  /* An `auto` track stretches into spare space by default, which would put the
     gap back the moment a row is narrower than the widest one. */
  justify-content:start;
  align-items:baseline; column-gap:.35rem;
}
.hero-preview-list .offering-title { display:contents; }
.hero-preview-list .offering-title>strong {
  min-width:0;
  font-size:1.02rem; font-weight:800; line-height:1.6;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* The second line stays one line: the university gives up its width and the
   city keeps all of its, because a truncated «تهران» says nothing while a
   truncated university name still names the university. Without this, a long
   university name pushed the city onto a third line and that row alone stood
   19px taller than the four beside it. */
.hero-preview-list .offering-at { font-size:.82rem; line-height:1.5; }
/* No hairline between them here. The divider earns its place on a row that
   reads as one line of three facts; on this two-line composition the one
   before the university opened the second line with a rule standing against
   nothing, and the one before the city held apart two halves of a single
   answer to «where». The city keeps its own ink instead, which is the
   distinction that was doing the work. */
.hero-preview-list .offering-at::before { content:none; }
/* The university gives up its width and the city keeps all of its: a truncated
   «تهران» says nothing, while a truncated university name still names the
   university. */
.hero-preview-list .offering-at {
  min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* What is said about it: one cell, always in the same column, and close enough
   to the name for the two to read as one row rather than as two islands. */
.hero-preview-list .offering-meta {
  flex-wrap:nowrap; justify-content:flex-start;
  gap:.3rem; margin:0;
}
.hero-preview-list .offering-tag { border:0; background:var(--paper); }
/* Quieter than the name it follows: it is a qualification on the row, not what
   the row is. */
.hero-preview-list .offering-tag-group .exam-group { font-size:.72rem; }
.hero-preview-list .demo-row-actions { flex:none; display:flex; }

/* Under 30rem the row is a card: the controls move to the end of the name's
   own line and the meta drops beneath, because three columns of content in a
   phone's width is three columns of one word each. */
@container preview (max-width:33.99rem) {
  .hero-preview-list .track-choice { row-gap:.15rem; }
  /* A finger, not a pointer: the two controls grow where the composition is
     the phone's, and the rank grows with them so the row keeps one height. */
  .hero-preview-list .demo-row-actions { --action-h:40px; gap:.25rem; }
  .hero-preview-list .track-rank { width:2.25rem; min-width:2.25rem; height:2.25rem; }
  .hero-preview-list .choice-drag-handle { height:2.25rem; }
}
/* Above it: lead, identity, metadata, the slack, then the controls. The
   identity column is `max-content` capped by the room there is, so it is as
   wide as the widest name in the list and no wider — the leftover width is the
   spacer's, not a gap driven between two related groups. */
/* Wide enough for the name and its group on one line: lead, name, group, the
   slack, then the controls. The name column is `max-content` capped by the
   room there is, so the group sits against the name it qualifies and the
   leftover width goes into the spacer rather than between the two. */
@container preview (min-width:34rem) {
  .hero-preview-list .track-choice {
    grid-template-columns:auto minmax(0,max-content) minmax(0,max-content) minmax(0,1fr) auto;
  }
  .hero-preview-list .offering-title>strong { grid-column:2; grid-row:1; }
  .hero-preview-list .offering-at { grid-column:2/4; grid-row:2; }
  .hero-preview-list .offering-meta { grid-column:3; grid-row:1; }
  .hero-preview-list .track-choice-lead { grid-row:1/span 2; }
  .hero-preview-list .demo-row-actions { grid-column:5; grid-row:1/span 2; }
}
/* Without container queries the phone composition is what everything gets: it
   is the one that survives any width. */
@supports not (container-type:inline-size) {
  .hero-preview-list .track-choice { grid-template-columns:auto minmax(0,1fr) auto; }
}

/* A finger is a finger at any width: the composition follows the column, but
   how big a control has to be follows the thing pressing it. A tablet at 768px
   gets the wide row *and* the larger controls. */
@media (pointer:coarse) {
  .hero-preview-list .demo-row-actions { --action-h:40px; gap:.25rem; }
  .hero-preview-list .track-rank { width:2.25rem; min-width:2.25rem; height:2.25rem; }
  .hero-preview-list .choice-drag-handle { height:2.25rem; }
}

/* One helper, the width of the list it explains ---------------------------
   It was a narrow centred paragraph under a full-width list, wrapping early
   into two short lines that belonged to nothing. It is a row now: the width of
   the rows above it, on its own tinted ground so it reads as part of this
   section rather than as the first sentence of the page below, and on one line
   wherever there is room for one. The glyph sits on the first line's own text
   rather than on the middle of the block. */
.hero-preview-note {
  display:flex; align-items:flex-start; gap:.45rem;
  margin:.55rem 0 0; padding:.5rem .7rem;
  border-radius:var(--radius-row); background:var(--brand-soft);
  color:#46536b; font-size:.78rem; line-height:1.7;
}
.hero-preview-note .action-icon {
  flex:none; width:14px; height:14px; margin-top:.28em; color:var(--brand-dark);
}
.hero-preview-note>span { min-width:0; }
/* A successful reorder acknowledges the touch: the moved row takes the soft
   brand surface for a beat and lets it go. Colour, not motion, so it reads
   the same under prefers-reduced-motion. */
.track-choice.is-moved { background:var(--brand-tint); }
@media (prefers-reduced-motion:no-preference) {
  .demo-row { transition:background-color .5s ease; }
}

/* Three steps, told as steps: a numbered circle, a claim, a sentence, and a
   thread between the circles. They were three bordered boxes, which is the
   shape this product uses for things you can act on, and there is nothing to
   act on here. */
/* One surface for the explanation and the button that follows it — a
   section boundary made of space and a change of surface, not a hairline
   repeated between every block on the page. */
.how-flow { margin-top:var(--space-2); padding:var(--space-2) 0 var(--space-4); }
.how-flow-panel {
  border:1px solid var(--line); border-radius:var(--radius-surface);
  background:var(--surface); padding:var(--space-4);
}
.how-flow h2 { margin:0; }
.section-lead { margin:.35rem 0 0; color:var(--muted); font-size:.92rem; }
.how-steps {
  list-style:none; display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  gap:1.5rem 2rem; margin:var(--space-4) 0 0; padding:0; counter-reset:flow;
}
.how-steps>li {
  counter-increment:flow; position:relative; min-width:0;
  display:grid; align-content:start; gap:.15rem;
}
.how-steps>li::before {
  content:counter(flow, persian);
  display:grid; place-items:center;
  width:2.5rem; height:2.5rem; margin-bottom:.45rem;
  padding-top:var(--numeral-shift);
  border-radius:var(--radius-control);
  background:var(--brand); color:#fff;
  font-size:1.2rem; font-weight:850; line-height:1;
}
/* The thread runs from a step back to the one before it, which in a
   right-to-left row is the neighbour on its inline-start side. The first step
   has nothing behind it, and once the three stack there is no row to thread. */
.how-steps>li::after {
  content:""; position:absolute; top:1.25rem; height:1px;
  inset-inline-start:-2rem; width:2rem;
  background:var(--brand-line);
}
.how-steps>li:first-child::after { content:none; }
.how-steps b { display:block; color:var(--ink); font-size:1.02rem; font-weight:800; line-height:1.7; }
.how-steps span { color:#46536b; font-size:.9rem; line-height:1.9; }
/* Three steps in three columns need the room for three columns: below it,
   `auto-fit` was free to land on two, stranding the third step alone on its
   own row. One column reads better than an orphan. */
@media (max-width:900px) {
  .how-steps { grid-template-columns:1fr; gap:1.15rem; }
  /* Stacked, the numeral goes back beside its own claim: a 40px square on a
     line of its own, three times down a phone, is three screens of numbering. */
  .how-steps>li {
    grid-template-columns:2.25rem minmax(0,1fr); column-gap:.85rem; align-items:baseline;
  }
  .how-steps>li::before {
    grid-column:1; grid-row:1/span 2; align-self:start;
    width:2.25rem; height:2.25rem; margin-bottom:0; font-size:1.05rem;
  }
  .how-steps b { grid-column:2; }
  .how-steps span { grid-column:2; }
  .how-steps>li::after { content:none; }
}

/* The first step, taken, inside the panel that just described it -------------
   The questionnaire was a section of its own two screens below, behind a
   disclosure whose closed state had to be dressed as a primary button to get
   pressed at all (owner, 2026-09-07). Here it is the end of the explanation:
   the same surface, a hairline and the panel's own rhythm between the steps
   and the doing of the first one. */
.how-flow-start {
  margin-top:var(--space-4);
  border-top:1px solid var(--line); padding-top:var(--space-4);
}
.how-flow-start:focus { outline:none; }
.how-flow-start>h3 { margin:0; color:var(--ink); font-size:1.1rem; font-weight:800; }
.how-flow-start>.help-text { margin:.3rem 0 1.3rem; }
.how-flow-start .guest-steps { margin:0; }

/* The page's last word repeats its first act. A heading, a sentence, one
   button the size of what it says — not a bar stretched edge to edge, which
   is a lot of empty middle for two short lines and a button to share. */
.closing-cta {
  display:flex; flex-wrap:wrap; align-items:center;
  gap:.6rem 1.5rem; margin-top:var(--space-4);
  border-top:1px solid var(--line); padding-top:var(--space-4);
}
.closing-cta-text { min-width:0; }
/* Its own section now rather than the last thing inside the «سه گام» panel,
   because it is the last thing on the page for every visitor and not only for
   a signed-in one. The rule above it is the section boundary; the margin the
   `.closing-cta` block already carries is the space. */
.closing-cta-section { margin-bottom:var(--space-5); }
.closing-cta h2,.closing-cta h3 { margin:0; color:var(--ink); font-size:1.15rem; }
.closing-cta p { margin:.25rem 0 0; color:var(--muted); font-size:.88rem; }
.closing-cta .button { flex:none; }

/* The product's own name, written inside a sentence ------------------------
   «مسیر» is an ordinary Persian word before it is a name, and a first-time
   reader has no way to tell which one a heading means (owner, 2026-09-07). It
   takes the product's own ink and weight — the same distinction `.brand-mark`
   makes in the header lockup, made in running text instead.

   Used on the home page's «سه گام» heading, which is where a stranger meets
   the name in a sentence for the first time. It is not applied to every «مسیر»
   in the product: extend it as that copy is touched, rather than sweeping
   templates that no reader has complained about. */
.product-name { color:var(--brand); font-weight:850; }

/* The one act the page is for, at the size of the decision it asks for. The
   height stays `--control-h`, because one height for every full-size button is
   what keeps a row of mixed controls lined up; what grows is the room around
   the words and the words themselves. */
.button-lead { padding-inline:1.7rem; font-size:1.02rem; }

/* The written content this same site publishes, named once at the bottom of
   the page the tool itself owns — three plain lists, not cards, so it reads
   as more reading rather than more product. */
.content-teaser { margin-top:var(--space-2); padding:var(--space-2) 0 var(--space-5); }
/* .section-heading is shared with panels that pair a heading with a short
   SIDE note (space-between, bottom-aligned) — right for a caption beside a
   title, wrong for an intro sentence introducing a whole section. Here the
   heading and its sentence are one stack, not two ends of a row. */
.content-teaser .section-heading { display:block; }
.content-teaser .section-heading h2 { margin-bottom:.5rem; }
.content-teaser .section-heading>p { max-width:640px; }
/* Equal-width, equal-padding cards, one per content type — the same surface
   the panel above already uses, so a type with fewer items yet (a single
   guide, say) reads as a smaller card doing its own job, not as bare page
   whitespace under a short list next to two long ones. Each card is a flex
   column so «مشاهده همه» always sits at the card's own bottom-left, however
   many items sit above it (grid row-stretch already equalizes card height). */
.content-teaser-grid {
  display:grid; grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));
  gap:var(--space-3); margin-top:var(--space-4);
}
.content-teaser-group {
  display:flex; flex-direction:column;
  border:1px solid var(--line); border-radius:var(--radius-surface);
  background:var(--surface); padding:var(--space-3);
  transition:border-color .18s ease;
}
.content-teaser-group:hover { border-color:var(--brand-line); }
/* Hierarchy inside the card: the type label reads first (bigger, brand-
   coloured, its icon on a tinted chip), the items are the body text, and
   «مشاهده همه» is the quietest thing on the card — it does not compete with
   the label that names what the card holds. */
.content-teaser-group h3 {
  display:flex; align-items:center; gap:var(--gap-glyph);
  margin:0 0 .55rem; padding-bottom:.6rem; border-bottom:1px solid var(--line);
  font-size:1.05rem; font-weight:800; color:var(--brand-dark);
}
/* How much is on the shelf, between its name and the five standing on it. It
   is what tells a reader the five are a sample and not the whole of it, and
   it is the sentence this product should be proudest of. */
.content-teaser-scale { margin:0 0 .9rem; color:var(--ink); font-size:.88rem; font-weight:700; line-height:1.7; }
.content-teaser-icon {
  flex:none; width:30px; height:30px; padding:.4rem; border-radius:8px;
  background:var(--brand-soft); color:var(--brand-dark);
}
/* Five titles that did not look like links (owner, 2026-09-07): ink at weight
   500 on a white card is exactly what this product's body text looks like, so
   the only thing saying they could be clicked was the cursor. They take what a
   link takes here — the brand ink, more weight, and a hover *surface* rather
   than an underline, which is this product's rule for a link — plus the
   chevron that says a row leads somewhere. The chevron is the shared SVG one:
   «‹» typed as a character is a letter, drawn at whatever size the typeface
   gives it. Pulled out to the card's own edge by a negative margin, so the
   hover surface reaches the full width of the column while the titles stay on
   the same start edge as the heading above them. */
.content-teaser-group ul { list-style:none; display:grid; gap:.1rem; margin:0 0 .8rem; padding:0; }
.content-teaser-group li { min-width:0; }
.content-teaser-group a:not(.text-link) {
  display:flex; align-items:center; gap:.5rem;
  margin-inline:-.5rem; border-radius:var(--radius-row); padding:.34rem .5rem;
  color:var(--brand-dark); font-weight:600; font-size:.92rem; line-height:1.7;
}
/* One line per title: a wrapped title in a five-row list makes the card taller
   than its neighbours and the chevrons stop lining up. */
.content-teaser-group a:not(.text-link)>span {
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.teaser-link-chevron {
  flex:none; width:14px; height:14px; margin-inline-start:auto;
  color:var(--brand); opacity:.5;
}
.content-teaser-group a:not(.text-link):hover,
.content-teaser-group a:not(.text-link):focus-visible {
  background:var(--brand-soft); color:var(--brand-strong);
}
.content-teaser-group a:not(.text-link):hover .teaser-link-chevron,
.content-teaser-group a:not(.text-link):focus-visible .teaser-link-chevron { opacity:1; }
.content-teaser-group a:not(.text-link):focus-visible { outline:2px solid var(--brand); outline-offset:2px; }
.content-teaser-group .text-link { align-self:flex-end; margin-top:auto; font-weight:600; font-size:.85rem; color:var(--ink-3); }
.content-teaser-group .text-link:hover, .content-teaser-group .text-link:focus-visible { color:var(--brand-dark); }

/* Below the pairing width the promise stacks above the sample. */
@media (max-width:1020px) {
  .hero { padding:2.5rem 0 2.25rem; gap:1.9rem; }
}

/* The rest of a long list -----------------------------------------------------
   A hundred and fifty rows is a page nobody scrolls and seven hundred controls
   nobody tabs through. The tail is there, in order, and out of the way until
   it is asked for. */
.track-more { display:flex; align-items:center; flex-wrap:wrap; gap:.5rem .8rem; margin:.8rem 0 0; }
/* Same: the button around it is a flex row (.button) with its own gap. */
.track-more-left { color:var(--muted); font-size:.8rem; font-weight:700; }
.track-more .text-button { color:var(--brand-dark); }

/* What high contrast erases, said again in its own inks --------------------
   Forced colors strips backgrounds and box-shadows, and this product draws
   its chevrons and its tick as backgrounds through a mask, tells a pressed
   segment apart by fill alone, and embeds a fixed dark chevron in the native
   select. Each gets the system's own language instead: the masked shapes opt
   out of the sweep and take a system ink, the select goes back to the arrow
   the system draws itself, and the pressed segment takes the same Highlight
   outline the checked selection card already does. */
@media (forced-colors:active) {
  details>summary::after,
  .row-source summary::before,.row-conditions summary::before { background:CanvasText; forced-color-adjust:none; }
  .select-trigger::after { background:ButtonText; forced-color-adjust:none; }
  .select-trigger:disabled::after { background:GrayText; }
  .option-row.is-selected::after { background:Highlight; forced-color-adjust:none; }
  select:not([multiple]) { appearance:auto; background-image:none; }
  .segmented button[aria-pressed="true"],.segmented a[aria-current] { outline:2px solid Highlight; outline-offset:-2px; }
}

/* Page needs gathered by the 1405-06 UX review ------------------------------ */
/* The unknown-city note and a refused rank are said at the field itself, in the
   inks the page already uses for «still waiting on you» and for errors. */
.home-place .place-warning { color:#8a4a0c; }
.account-essentials .exam-group-results input[aria-invalid="true"] { border-color:var(--danger); }
/* A retired thing (superseded, rejected, archived) needs no attention, so its
   pill is cold neutral: neither the warm unknown nor the red unavailable. */
.status-dormant { background:#edf1f6; color:#54637d; }

/* On a phone the favourite row wraps instead of squeezing the offering card
   into a sliver beside its fixed action group. */
@media (max-width:520px) {
  .favorite-list li { flex-wrap:wrap; }
  .favorite-row-actions { width:100%; justify-content:flex-start; }
}
/* Below 820px the table head is gone, and with it the only sort control, so
   each control bar carries a select that emits the same sort URLs. */
.catalog-sort-control { display:none; }
@media (max-width:820px) { .catalog-sort-control { display:flex; } }
/* The source citation is the longest fact in the detail grid and owns the row. */
.row-detail-facts .row-detail-source { grid-column:1 / -1; }
/* In the card layout a row and its open detail are one thing, not two cards. */
@media (max-width:820px) {
  .catalog-row.is-expanded { margin-bottom:0; border-end-start-radius:0; border-end-end-radius:0; border-bottom:0; }
  .catalog-row-detail:not([hidden]) { margin-top:0; border-start-start-radius:0; border-start-end-radius:0; }
}
/* The order comparison numbers its rows in the list's own digits and marks a
   moved row by ink alone. */
.order-comparison ol { list-style:none; padding:0; margin:0; counter-reset:cmp; }
.order-comparison li::before { counter-increment:cmp; content:counter(cmp, persian) '. '; color:var(--brand); }
.order-comparison li.is-moved { color:var(--brand-dark); font-weight:700; }
.order-comparison li small { color:var(--muted); font-weight:400; }
.collaborator-form .help-text { grid-column:1/-1; margin:0; font-size:.8rem; }
/* Required information wears the warm tone, beside the field that wants it. */
.field-required-note { margin:.4rem 0 0; border:1px solid #ecdcb4; border-radius:9px; background:#fff9e8; color:#735c25; padding:.5rem .7rem; font-size:.9rem; font-weight:700; }
.export-warning-errors { margin:.6rem 0 0; }
.export-warning-errors strong { color:#8a241b; }
/* The inverse of .no-print: the printed sheet's identity line, invisible on screen. */
.print-only { display:none; }
@media print { .print-only { display:block; } }
.copy-status { margin-inline-start:var(--gap-badge); font-size:.9rem; }
.copy-status.is-copied { color:#1a6a51; font-weight:700; }
.copy-status.is-copy-failed { color:#735c25; font-weight:700; }
/* The printed sheet ----------------------------------------------------------
   The page a reader takes to the desk where they type their choices into the
   official system. It was the screen page with the header removed: full-bleed
   to the paper's edge, the demo-data notice printed at the top of it, one
   choice every three lines, and every warning carried by a background colour
   that a printer drops. Under an هزار-row list that is many pages of very
   little.

   `print-color-adjust` is asked for and not relied on: every marking here also
   says what it is in words (`.export-over-flag`, «ضروری»/«یادآوری» in the
   audit), so a sheet printed in grayscale by a browser that honours the
   reader's ink-saving setting still carries the same information. */
@page { margin:14mm 12mm; }
@media print {
  body { background:#fff; }
  :root { print-color-adjust:exact; -webkit-print-color-adjust:exact; }
  /* Nothing on paper is about this browser session: a notice with a close
     button on it is a control, and the reminder that the data is last year's
     belongs to the screen the reader is choosing on. */
  .site-banner { display:none !important; }
  /* Paper is narrower than a desktop window and has no scrollbar, so the type
     that reads well on screen is a size too large here. */
  html { font-size:11.5pt; }
  .export-head h1 { font-size:1.5rem; }
  .export-track h2 { font-size:1.15rem; }
  .export-table { font-size:.85rem; }
  .export-table th,.export-table td { padding:.28rem .45rem; }
  /* A table split across a page break keeps its column names on both halves,
     which is the whole reason a long list is a table. */
  .export-table thead { display:table-header-group; }
  .export-table tr { break-inside:avoid; }
  /* A heading is never the last thing on a page. */
  .export-track h2,.export-track-count,.export-over-note { break-after:avoid; }
  /* Each list starts its own sheet: two lists of a hundred and fifty rows are
     two documents, and they are typed into two different forms. */
  .export-track+.export-track { break-before:page; }
  .export-audit { break-before:page; margin-bottom:0; }
  .source-disclaimer { font-size:.8rem; }
}
/* The workspace status reports on actions taken anywhere in two long lists, so
   while it has something to say it stands at the bottom edge, floating: the
   shadow is the allowance for what genuinely floats. */
.save-status-docked:not(:empty) {
  position:fixed; bottom:1rem; right:50%; transform:translateX(50%);
  z-index:60; margin:0; width:max-content; max-width:min(680px, calc(100vw - 2rem));
  display:flex; align-items:center; flex-wrap:wrap; gap:.4rem .7rem;
  box-shadow:0 10px 30px rgba(23,63,95,.2);
}
/* The rename control sits beside the heading, so the heading's name is the
   plan's name alone. */
.plan-title-row { display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; min-width:0; }
.plan-title-row:hover .title-rename { border-color:var(--brand-line); color:var(--brand-dark); }
.workspace-noscript { margin-bottom:1.5rem; border:1px solid #e2d3ae; border-radius:14px; background:#fffaf0; padding:.85rem 1.1rem; color:#5c4a22; }
/* A full list is admitted to before browsing starts, not in the footer after. */
.matches-cap-note { margin:0 0 .8rem; border:1px solid #e2d3ae; border-radius:12px; background:#fffaf0; padding:.7rem .95rem; color:#5c4a22; font-size:.88rem; line-height:1.85; }
.matches-cap-note[hidden] { display:none; }
.matches-feedback { flex:1 1 100%; margin:0; }
/* No margin: the summary that holds it is already a flex row with its own
   gap, and a margin on top of a gap is that gap said twice. */
.match-group-kind { color:var(--muted); font-size:.8rem; font-weight:400; }
/* Account-gated controls stay focusable so the reason is hearable; they look
   exactly as disabled did. */
.toolbar-button[aria-disabled="true"],.workspace-toolbar .button[aria-disabled="true"] { opacity:.45; cursor:not-allowed; }
.toolbar-button[aria-disabled="true"]:hover { border-color:var(--line); background:transparent; }

/* Page-level notices --------------------------------------------------------
   Each notice is its own strip: two present means two stacked banners (each
   carries its own top margin, so they stack with a gap between them), never
   one shared box arguing for both at once. Tone is per-strip (`is-warm` for
   last year's data — stays in print; `is-info` for the dev-mode aside). */
.site-banner {
  width:var(--page-w); margin:.5rem auto 0;
  /* `start`, not `center`: on a phone the message is three lines and the
     control that closes it belongs beside the first of them, not floating at
     the middle of the block. */
  display:flex; flex-wrap:wrap; align-items:start; justify-content:space-between; gap:.15rem 1.4rem;
  border:1px solid var(--line); border-radius:10px; background:var(--surface);
  padding:.4rem .75rem; color:var(--muted); font-size:.78rem; line-height:1.7;
}
/* The message shares the line with the close control instead of taking the
   whole of it. At its natural width it is wider than a phone, so with a
   `0 1 auto` basis it filled the row on its own and pushed the «×» onto a
   line of its own: 41px of an empty strip, twice, at the top of every
   page. `1 1 0` lets it wrap inside the space that is left. */
.site-notice { display:inline-flex; align-items:baseline; flex:1 1 0; min-width:0; flex-wrap:wrap; gap:.1rem .45rem; margin:0; }
.site-banner strong { flex:none; font-weight:800; color:var(--ink); }
.site-banner.is-info { border-color:var(--brand-line); background:var(--brand-soft); color:var(--brand-dark); }
.site-banner.is-info strong { color:var(--brand-dark); }
.site-banner.is-warm { border-color:#e3c37c; background:#fff8e6; color:#684d13; }
.site-banner.is-warm strong { color:#7a5c14; }
.notice-dismiss {
  display:inline-flex; align-items:center; justify-content:center;
  /* Whole pixels, and the 24px a pointer target may not go under. It was
     1.4rem — 22.4px, fractional, so the glyph inside it rasterised off the
     pixel grid, and under the minimum besides. No margin: the strip around it
     is a flex row that already spaces its own children. */
  width:24px; height:24px; flex:none; padding:0;
  border:0; border-radius:6px; background:transparent; color:inherit; opacity:.6; cursor:pointer;
}
/* The mark stays 24px, which is all a notice strip has room for; the target
   around it is 44. A pseudo-element takes no space in the flex row, so the
   strip does not grow to hold a hit area. */
.notice-dismiss { position:relative; }
.notice-dismiss::before { content:""; position:absolute; inset:-10px; }
.notice-dismiss:hover { opacity:1; background:rgba(0,0,0,.06); }
.notice-dismiss:focus-visible { opacity:1; background:var(--brand-soft); color:var(--brand-dark); }
.notice-dismiss .action-icon { width:12px; height:12px; }
@media print {
  .site-banner.is-warm { border-color:#999; background:transparent; }
}

/* The header's near side: the logo, then who this is and who made it,
   the same pairing the footer closes with, plus the one word that matters
   most to somebody who has never seen the product before. -------------- */
.site-header-lead { display:flex; align-items:center; gap:.85rem; min-width:0; }
/* The name says «مسیر» and nothing else, so where it comes from has to be said
   beside it — at every width, not only where the full lockup fits. Below 900px
   the attribution is the whole of it. */
.brand-lockup { display:flex; margin:0; min-width:0; flex-direction:column; gap:.05rem; line-height:1.35; }
.brand-lockup .brand-title { display:none; }
/* Below the width that fits the full lockup beside the name, the credit sits
   under it instead of squeezed next to it — «محصولی از فرادرس» broke over
   three lines in the space that was left. One line, always. */
@media (max-width:899px) {
  .site-header-lead { flex-direction:column; align-items:flex-start; gap:0; }
  .brand-attribution { white-space:nowrap; }
}
/* The product's own name, wherever it is written -----------------------------
   «مسیر» is the name; everything around it is description. It is set apart by
   weight and by the product's own ink, not by a different typeface — a second
   face in a two-word lockup reads as a logo pasted onto a page rather than as
   the page's own voice. */
.brand-mark { color:var(--brand); font-weight:850; letter-spacing:0; }
/* «نرم‌افزار رایگان انتخاب رشته هوشمند» is four words longer than what stood
   here, so the line it sits on gets a size that still fits it above 900px,
   where this half of the lockup is drawn at all. */
.brand-title { color:var(--ink); font-size:.76rem; font-weight:750; white-space:nowrap; }
.brand-attribution { color:var(--muted); font-size:.72rem; font-weight:600; }
/* «فرادرس» inside «محصولی از فرادرس» (owner, 2026-09-07) ---------------------
   Where the product came from is a name too, so it is written as one — in the
   parent's actual logotype, `assets/faradars-wordmark.svg`. That file is the
   wordmark lifted out of the logo FaraDars publishes (six paths, its own
   #3064C0), so nothing here is redrawn or approximated in a typeface, and the
   mark keeps its own colour rather than borrowing this product's: two names on
   one line must not read as one.

   Sized in `em`, so it follows the line it sits on — the header sets that line
   at .72rem and the footer at .9rem, and the mark is the same size relative to
   both without either being told. The `alt` is the word, so the sentence still
   reads «محصولی از فرادرس» with images off. */
/* Sized against the words beside it, not against the line box: at 1.3em the
   mark stood visibly taller than «محصولی از» next to it (owner, 2026-09-07).
   A fifth smaller settles it. The baseline offset comes down with it — the
   «س» descender is a fixed share of the mark's own height, so the two move
   together or the word starts floating above its line. */
.faradars-mark {
  display:inline-block; width:auto; height:1.05em;
  vertical-align:-.19em;
}
/* A logo does not take the ink of the text around it, so the footer's link
   answers a hover the one way a fixed-colour mark can. Its underline is
   declined where paragraph links are underlined, further up this sheet. */
/* The credit is a link, and the mark is 21px tall — half the 44px every other
   link in this footer stands at, and under the minimum the design system asks
   of a touch target. Expanded the way `.notice-dismiss` does it: an invisible
   box around it, so the hit area grows and nothing in the layout moves. The
   only thing it can overlap is the tagline underneath, which is prose and
   takes no clicks of its own. */
.faradars-link { display:inline-block; text-decoration:none; position:relative; }
.faradars-link::before { content:""; position:absolute; inset:-12px -8px; }
.faradars-link:hover .faradars-mark,
.faradars-link:focus-visible .faradars-mark { opacity:.72; }
@media (min-width:900px) {
  .brand-lockup { padding-inline-start:.85rem; border-inline-start:1px solid var(--line); }
  .brand-lockup .brand-title { display:block; }
}

/* The header's far side: the navigation, then the signed-in menu ------------ */
.site-header-side { display:flex; align-items:center; gap:.6rem; min-width:0; }

/* The profile menu -----------------------------------------------------------
   A <details> under the reader's own name: the signed-in places once each, and
   the way out of the account, on every page. It opens without script; script
   only closes it from outside and from Escape. */
.profile-menu { position:relative; flex:none; }
/* A menu trigger, not a fourth nav link: a hairline border at rest marks it
   as its own control (the same "border says container" language the picker
   fields and button-quiet already use), so it doesn't need the pointer down
   on it to be told apart from فهرست/راهنماها/دانشگاه‌ها beside it. */
.profile-menu summary {
  list-style:none; display:inline-flex; align-items:center; gap:.3rem;
  min-height:44px; border:1px solid var(--line); border-radius:9px; padding:.4rem .55rem;
  color:var(--brand-dark); font-weight:700; white-space:nowrap; cursor:pointer;
}
.profile-menu summary::-webkit-details-marker { display:none; }
/* The menu draws its chevron as an inline icon that rotates open; the generic
   disclosure chevron would be a second one beside it. */
.profile-menu summary::after { content:none; }
.profile-menu summary:hover,
.profile-menu[open] summary { background:var(--brand-soft); color:var(--brand); }
.profile-menu summary .chevron-icon { width:14px; height:14px; transition:transform .16s ease; }
.profile-menu[open] summary .chevron-icon { transform:rotate(180deg); }
/* The trigger wears the reader's own name, and a name is a value this product
   does not control the length of — a Persian given name with a patronymic, or
   an email address, would push the bar wide enough to shove the navigation off
   it. So the label is capped in `ch`, which measures in the font's own
   characters and therefore holds roughly the same number of them whatever the
   width is set to, and what does not fit is clipped rather than wrapped: the
   bar is one line high and must stay one line high. Nothing is lost to the
   clip: design-system.js measures the label and, only when it finds it cut
   off, offers the whole value as the shared tooltip.

   11ch is measured, not guessed. «حساب کاربری», the fixed label this replaces,
   is eleven characters, and at 390px it drew 80px; 11ch is 93px, because `ch`
   is the advance of «0» and runs wider than the average Persian glyph. Those
   13px come out of slack the row already had, not out of the brand: measured
   in a 390px frame, the trigger grows 118px → 131px while `.site-header-lead`
   holds 70px either way and the document still needs only 375px of 390. A long
   name and a long address both land on the same 131px, because the cap decides
   the width and the content does not. Nothing here can overflow the row in any
   case — `.site-header-lead` is `min-width:0; flex:1 1 auto`, so the brand
   yields first and this control (`flex:none`) is the only rigid thing on the
   bar.

   The cap is tight below 900px, and not because the phone is the narrow case:
   the squeeze is 761-900px, where the navigation row is already back (it
   returns at 761px) but the bar is still too narrow for the brand lockup's
   tagline. Four Persian labels, the lockup and this control share that width,
   so the label yields there and only opens up at 900px — the point the lockup
   itself judges the bar roomy enough for its second line. */
.profile-menu-name {
  display:block; max-width:11ch;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
@media (min-width:900px) { .profile-menu-name { max-width:22ch; } }
/* One surface for every menu that drops out of the header bar, so the account
   menu and the navigation menu beside it are the same object at two labels
   rather than two boxes that happen to look alike. */
.profile-menu-panel,
.nav-menu-panel {
  position:absolute; top:calc(100% + .4rem); inset-inline-end:0; z-index:80;
  display:grid; min-width:215px;
  border:1px solid var(--line); border-radius:12px;
  background:var(--surface); box-shadow:var(--shadow-pop); padding:.35rem;
}
.profile-menu-panel a,
.profile-menu-panel button,
.nav-menu-panel a {
  display:flex; align-items:center; gap:var(--gap-glyph); width:100%; min-height:40px;
  margin:0; border:0; border-radius:9px; background:transparent;
  padding:.45rem .6rem; color:var(--ink); font:inherit; font-size:.92rem;
  font-weight:700; text-decoration:none; text-align:start; cursor:pointer;
}
.profile-menu-panel a:hover,
.profile-menu-panel button:hover,
.nav-menu-panel a:hover { background:var(--brand-soft); color:var(--brand-dark); }
.nav-menu-panel a[aria-current] { background:var(--brand-soft); color:var(--brand); }
.profile-menu-panel .action-icon { width:16px; height:16px; flex:none; color:#8195b3; }
.profile-menu-panel a:hover .action-icon,
.profile-menu-panel button:hover .action-icon { color:var(--brand-dark); }
/* Leaving is the one act here that is not a place, so a hairline stands it off. */
.profile-menu-signout { border-top:1px solid var(--line); margin-top:.25rem; padding-top:.3rem; }
.profile-menu-signout button { color:var(--danger); }
.profile-menu-signout button .action-icon { color:var(--danger); }
.profile-menu-signout button:hover { background:#fdf1ef; color:#8a3a30; }
.profile-menu-signout button:hover .action-icon { color:#8a3a30; }
/* The navigation menu ---------------------------------------------------------
   The same four destinations the row above holds, for the width where a row of
   four Persian labels does not fit. Only ever one of the two is displayed, so
   the accessibility tree only ever holds one «ناوبری اصلی». The toggle borrows
   the profile menu's trigger language, because they sit side by side and are
   the same kind of control. */
.nav-menu { display:none; position:relative; flex:none; }
.nav-menu-toggle {
  list-style:none; display:inline-flex; align-items:center; gap:var(--gap-glyph);
  min-height:44px; border:1px solid var(--line); border-radius:9px;
  padding:.4rem .6rem;
  color:var(--brand-dark); font-weight:700; white-space:nowrap; cursor:pointer;
}
.nav-menu-toggle::-webkit-details-marker { display:none; }
/* Its own glyph, so the generic disclosure chevron would be a second one. */
.nav-menu-toggle::after { content:none; }
.nav-menu-toggle:hover,
.nav-menu[open] .nav-menu-toggle { background:var(--brand-soft); color:var(--brand); }
.nav-menu-panel { inset-inline-start:0; inset-inline-end:auto; min-width:min(15rem,calc(100vw - 2rem)); }

/* The header's far side is one size ----------------------------------------
   The destinations, the two menu triggers and the sign-in button all sit in
   one strip and all open or lead to panels set in `.92rem`, so that is what
   the strip is set in. They took the body's own 16px, which made every item on
   this side of the bar the largest text in the header — larger than the items
   inside the menus they open, and larger than the product's own name at
   1.12rem. The bar is chrome: it is read by shape and position far more than
   by size, and 16px spent on «راهنماها» is 16px not spent on the page. */
.site-header-side { font-size:.92rem; }
.site-header .main-nav>a:not(.button),
.profile-menu summary,
.nav-menu-toggle { font-size:.92rem; }

/* The three content sections wear their own accent -------------------------
   Hover and current-page marking on «راهنماها», «دانشگاه‌ها» and
   «رشته‌های دانشگاهی» use that section's colour; «فهرست رشته‌محل‌ها», the
   sign-in button and everything else in the bar keep the product blue
   (owner, 2026-09-06). It is a promise about where the click lands: the page
   that opens is painted in the colour the link just showed.

   ONE rule serves all three. --type-color and --c-wash come from the
   page-level [data-cat] theme block (catThemeCss), and each link carries its
   own data-cat, so the three values are the types' stored colours rather than
   three hexes copied into this file to fall out of date with them. The
   product layout emits that block now too — see setTypeThemeCss in
   src/render.ts.

   Placed after the plain .main-nav and .nav-menu-panel hover rules, which it
   ties with on specificity: same weight, later wins. */
.site-header .main-nav>a[data-cat]:hover,
.site-header .main-nav>a[data-cat][aria-current],
.nav-menu-panel a[data-cat]:hover,
.nav-menu-panel a[data-cat][aria-current] {
  color:var(--type-color);
  background:var(--c-wash);
}

@media (max-width:760px) {
  .site-header-side { width:100%; justify-content:flex-end; gap:.5rem; }
  /* The row gives way to the menu. `display:none` rather than a visual trick,
     so the hidden copy leaves the accessibility tree with it. */
  .site-header .main-nav { display:none; }
  .nav-menu { display:block; }
}

/* Breathing room where surfaces stack directly on the page ------------------ */
main > * + .panel,
main > * + .account-form { margin-top:1.6rem; }
.account-form > .panel + .panel { margin-top:1.6rem; }
#shortcuts-title { margin-top:2.6rem; }
.plan-list { margin-bottom:1rem; }

.disclaimer-page p { margin:0; line-height:2; }


/* Short-lived feedback ------------------------------------------------------
   Confirmations float over the page near its top and leave on their own, so
   nothing in the layout moves for a message that lasts four seconds. */
.toast-region {
  /* Under whatever is pinned above the page, never at the window's own top:
     `--sticky-top` is the one place that height is known, and an admin bar is
     the thing it is usually holding. */
  position:fixed; top:calc(var(--sticky-top) + .9rem); inset-inline:0; z-index:130;
  display:grid; justify-items:center; gap:.45rem;
  pointer-events:none;
}
.toast {
  display:inline-flex; align-items:center; gap:.45rem;
  max-width:min(34rem,calc(100vw - 2rem)); margin:0;
  border:1px solid var(--line); border-radius:999px;
  background:var(--surface); box-shadow:var(--shadow-pop);
  padding:.5rem 1rem; color:var(--ink); font-size:.88rem; font-weight:600;
  transition:opacity .18s ease, transform .18s ease;
}
.toast .action-icon { width:15px; height:15px; flex:none; color:var(--success); }
.toast.is-success { border-color:#bcd8cd; }
/* Work still running: the same surface, without the tick that says «done». */
.toast.is-pending { border-color:var(--line); color:var(--muted); font-weight:500; }
/* The one move that repairs a failure, inside the message that reports it.
   A toast carrying one is not dismissed on a timer, so the button is always
   still there when it is reached for; `pointer-events` has to be given back,
   because the region above it is transparent to the pointer by design. */
.toast-action {
  flex:none; margin-inline-start:.25rem; border:1px solid currentColor;
  border-radius:999px; background:transparent; padding:.1rem .6rem;
  color:inherit; font:inherit; font-size:.82rem; font-weight:700; cursor:pointer;
  pointer-events:auto;
}
.toast-action:hover { background:rgba(255,255,255,.5); }
.toast.is-error { border-color:#e0b7b1; background:#fdf3f1; color:#8a3a30; }
.toast.is-entering { opacity:0; transform:translateY(-.4rem); }
.toast.is-leaving { opacity:0; transform:translateY(-.4rem); }
@media (prefers-reduced-motion:reduce) { .toast { transition:none; } }
@media print { .toast-region { display:none !important; } }

/* A dragged row paints above the neighbour sliding past it ----------------- */
.is-reordering .is-dragging { position:relative; z-index:6; }

/* Needs-update: a quiet pulse inside the button, not a ring around it ------- */
.update-dot {
  display:inline-block; flex:none; width:8px; height:8px;
  border-radius:999px; background:#ffd60a;
  animation:update-dot-pulse 1s ease-in-out infinite;
}
@keyframes update-dot-pulse {
  0%, 100% { opacity:1; }
  50% { opacity:.15; }
}
@media (prefers-reduced-motion:reduce) { .update-dot { animation:none; } }

/* The claim-draft panel is the first-run surface when a draft exists -------- */
.claim-draft .claim-draft-actions { display:flex; align-items:center; flex-wrap:wrap; gap:.5rem 1rem; margin-top:.8rem; }

/* The full boundary statement reads in sections across the width ----------- */
.disclaimer-page {
  max-width:none;
  display:grid; grid-template-columns:repeat(auto-fit,minmax(min(26rem,100%),1fr));
  gap:1.4rem 3rem;
}
.disclaimer-page section { max-width:68ch; }
.disclaimer-page h2 { margin:0 0 .35rem; font-size:1.05rem; }

/* Published content pages ---------------------------------------------------
   The public half of the mini-CMS: a section listing and a single document.
   Prose surfaces, so everything is measured for reading: one column, a
   comfortable line length, and the page's own quiet ink for the metadata. */
.content-list { display:grid; gap:1rem; max-width:820px; padding-bottom:6rem; }
.content-list-item { border-radius:18px; box-shadow:none; padding:1.3rem 1.5rem; }
.content-list-item h2 { font-size:1.3rem; margin-bottom:.3rem; }
.content-list-item h2 a { color:var(--brand-dark); text-decoration:none; }
.content-list-item h2 a:hover { text-decoration:underline; }
.content-list-item p { margin:0 0 .45rem; color:var(--muted); max-width:68ch; }
.content-date { margin:0; color:var(--muted); font-size:.88rem; }
.content-page { max-width:820px; padding-bottom:6rem; }
.content-page .workspace-head { padding-bottom:1.4rem; }
.content-lead { max-width:68ch; color:var(--muted); font-size:1.05rem; margin-bottom:.6rem; }
.content-body { border-radius:18px; box-shadow:none; }
.content-body p { max-width:68ch; margin:0 0 1.2rem; line-height:2; }
.content-body p:last-child { margin-bottom:0; }
.content-return { margin-top:1.6rem; }
.content-view-link { display:inline-block; margin-top:.4rem; color:var(--brand-dark); font-weight:700; font-size:.88rem; }


/* The plan bar's four answers ------------------------------------------------
   Above 760px this disclosure is not a disclosure: `display:contents` takes it
   and its panel out of the box tree, so the four buttons are direct children
   of the toolbar's flex row and lay out exactly as they did before it existed.
   assets/js/design-system.js is what holds it open there — a closed <details>
   hides its panel whatever `display` says. */
.toolbar-answers { display:contents; }
.toolbar-answers-toggle { display:none; }
.toolbar-answers-panel { display:contents; }

/* The catalog's filters ------------------------------------------------------
   Above 760px this is not a disclosure at all: the details and its panel are
   `display:contents`, so each picker stays a cell of the panel's own grid. */
.catalog-filter-more { display:contents; }
.catalog-filter-toggle { display:none; }
.catalog-filter-more-panel { display:contents; }
/* Chrome puts a box of its own between a <details> and its content
   (`::details-content`), so `display:contents` on the panel alone still left
   every picker inside one grid cell — the eight of them stacked in a single
   column across a desktop panel built for four. The UA box has to be taken out
   of the box tree as well. Unknown to engines that do not implement it, which
   ignore the rule and are unaffected: there the panel div is the only wrapper
   and `display:contents` on it is already enough. */
@media (min-width:761px) {
  .catalog-filter-more::details-content,
  .toolbar-answers::details-content { display:contents; content-visibility:visible; }
}

@media (max-width:760px) {
  /* One control, with the number of answers it is holding on it. The search
     field above it never folds — typing a name is how most readers arrive,
     and Enter submits the form without any of this being opened. */
  .catalog-filter-more { display:block; grid-column:1/-1; }
  .catalog-filter-toggle {
    list-style:none; display:flex; align-items:center; gap:var(--gap-badge);
    min-height:var(--control-h); border:1px solid var(--field-line);
    border-radius:var(--radius-control); background:#fff; padding:.5rem .75rem;
    color:var(--brand-dark); font-weight:800; cursor:pointer;
  }
  .catalog-filter-toggle::-webkit-details-marker { display:none; }
  .catalog-filter-toggle::after {
    content:""; margin-inline-start:auto; width:14px; height:14px;
    background:currentColor;
    -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='m5 7.5 5 5 5-5' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
    mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='m5 7.5 5 5 5-5' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
    transition:transform .16s ease;
  }
  .catalog-filter-more[open] .catalog-filter-toggle::after { transform:rotate(180deg); }
  .catalog-filter-count {
    display:inline-flex; align-items:center; justify-content:center;
    min-width:1.4rem; min-height:1.4rem; border-radius:999px;
    background:var(--brand); color:#fff; font-size:.76rem; font-weight:800; line-height:1;
  }
  .catalog-filter-more-panel { display:grid; gap:.7rem; margin-top:.7rem; }
  .catalog-filter-actions { display:grid; gap:.5rem; }
}

/* A word a wide screen has room for and a narrow one does not. The sentence
   still reads without it, which is the only reason a word may be dropped. */
.on-wide { display:inline; }
@media (max-width:760px) { .on-wide { display:none; } }

@media (max-width:760px) {
  /* One control instead of four, and the bar comes back to a single row. */
  .toolbar-answers { display:block; position:relative; flex:none; }
  .toolbar-answers-toggle {
    list-style:none; display:inline-flex; align-items:center; gap:var(--gap-badge);
    min-height:var(--action-h); border:1px solid var(--field-line); border-radius:10px;
    background:#fff; padding:.35rem .7rem;
    color:var(--brand-dark); font-size:.86rem; font-weight:700; white-space:nowrap; cursor:pointer;
  }
  .toolbar-answers-toggle::-webkit-details-marker { display:none; }
  .toolbar-answers-toggle:hover,
  .toolbar-answers[open] .toolbar-answers-toggle { border-color:var(--brand); background:var(--brand-soft); }
  .toolbar-answers-panel {
    position:absolute; z-index:80; top:calc(100% + .4rem); inset-inline-start:0;
    display:grid; gap:.3rem; min-width:min(15rem,calc(100vw - 2rem));
    border:1px solid var(--line); border-radius:12px;
    background:var(--surface); box-shadow:var(--shadow-pop); padding:.35rem;
  }
  .toolbar-answers-panel .toolbar-button { justify-content:flex-start; border-color:transparent; }

  /* What is left is where you are and the one act that follows from changing
     an answer, on one line. */
  .workspace-toolbar { flex-wrap:nowrap; gap:.4rem; padding:.45rem .5rem; }
  .workspace-toolbar-rule { display:none; }
  .workspace-toolbar-spacer { display:none; }
  .workspace-sections { flex:1 1 auto; min-width:0; overflow-x:auto; scrollbar-width:none; }
  /* A destination's name is one line: wrapped, it made the bar it sits in
     taller than the row it belongs to. */
  .workspace-sections a { white-space:nowrap; }
  .workspace-sections::-webkit-scrollbar { display:none; }
  .workspace-toolbar>.button { flex:none; }
  /* An anchor inside the page must clear the bar that is stuck over it. */
  .decision-section { scroll-margin-top:calc(var(--sticky-top) + 5.5rem); }

  /* The row of facts under an offering, one step denser: the same tags, the
     same order, two or three lines instead of five. Nothing is dropped —
     capacity, competition, group and code are what the choice is made on. */
  :root { --chip-gap:.25rem; }
  .offering-tag,
  .group-badge,
  .competition { min-height:22px; padding:.05rem .4rem; font-size:.74rem; }
  .track-choice { gap:.5rem .6rem; padding:.6rem .65rem; }
  .track-choice-body>small { line-height:1.6; }
}

/* ===========================================================================
   The narrow screen
   ---------------------------------------------------------------------------
   One place for what a phone needs, rather than a rule per component scattered
   through the file. Everything here is a change of DENSITY — spacing, wrapping,
   how many of a repeating sample is worth showing — never of content or of what
   a control can do. The page above 760px is untouched by all of it.
   =========================================================================== */
@media (max-width:760px) {
  /* The rhythm the whole page is built on, one step tighter. Because every
     page-level section already spaces itself from these four tokens, this is
     the single lever that takes the air out of every section at once instead
     of a padding override per page. */
  :root {
    --space-1:.4rem; --space-2:.6rem; --space-3:.9rem; --space-4:1.4rem; --space-5:2rem;
  }

  /* --- Notices ------------------------------------------------------------
     A strip, not a block. The title stops owning a line of its own and leads
     the sentence it belongs to, which is how it would be read aloud anyway;
     the leading comes in from 1.7, which is a reading measure for a paragraph
     and far too airy for two lines of small print. Nothing is hidden. */
  .site-banner { padding:.4rem .55rem; }
  .site-notice { display:block; line-height:1.55; }
  .site-banner strong { display:inline; }
  .site-notice>span { display:inline; }
  .site-banner strong::after { content:" "; }

  /* --- The hero -----------------------------------------------------------
     The headline breaks where the sentence does, instead of leaving one word
     alone on the second line. The lead paragraph loses the 2.0 leading it
     wears on a wide column, which on a 40-character line reads as gaps. */
  .hero { padding:1.4rem 0 1.2rem; gap:1.4rem; }
  .hero h1 { text-wrap:balance; line-height:1.35; }
  .hero-copy .lead { line-height:1.85; }
  .hero-copy .actions { margin-top:1.1rem; }
  .hero-copy .actions .button { flex:1 1 auto; }
  .hero-stats { gap:.35rem 1.6rem; margin-top:1.1rem; }
  .hero-scope { margin-top:.9rem; line-height:1.8; }

  .hero-preview-note { margin-top:.5rem; line-height:1.7; }

  /* --- How it works ------------------------------------------------------- */
  .how-flow-panel { padding:1.1rem 1rem; }
  .how-steps span { line-height:1.8; }
  .how-steps b { line-height:1.5; }
  .how-flow-start { margin-top:1.3rem; padding-top:1.3rem; }

  /* --- Anything a fingertip has to land on --------------------------------
     44px, not the 39–41 these carry on a screen driven by a cursor. The tick
     box's whole label is the target and always was; what it lacked was the
     height. */
  .check-label { min-height:44px; }
  .back-link { min-height:44px; }
  .profile-menu-panel a,
  .profile-menu-panel button,
  .nav-menu-panel a { min-height:44px; }

  /* --- The content cards --------------------------------------------------
     Four names is enough of a sample to say what a section holds; the link
     that opens the whole of it is right underneath. */
  .content-teaser-group { padding:1rem; }
  .content-teaser-group ul { gap:.35rem; }
  .content-teaser-group ul>li:nth-child(n+5) { display:none; }
}

/* The grip is on every row at every width -----------------------------------
   It used to be hidden below 760px, on the reasoning that two ways to move a
   row is one too many on a narrow screen and the arrows already cover it. That
   is a real trade, and it is the wrong side of it: a control that exists on a
   desktop and not on a phone teaches a reader something about the product that
   stops being true when they pick up a different device, and «you can drag
   these» is exactly the affordance a touch screen makes most sense of. The row
   is narrower than it was — the strip is one paired control now rather than
   two separate buttons — so the width it needs is there. Its size is set once,
   under `pointer:coarse`, and not again here: this block used to re-set it to
   2rem and a third rule to 36px, so the fingertip-sized target declared up
   there never reached a phone at all. */
@media (max-width:760px) {
  /* Two ways to move a row, and a phone has room for one. The grip is the one
     that stays: it is the gesture a touch screen is for, it is on every row at
     every width, and dropping the arrows gives the row back ۷۰px of the width
     a phone does not have. Scoped to the plan's own lists — the sample on the
     way in is a demonstration of *both* ways and keeps them. Keyboard reorder
     is unaffected: it runs on the grip. */
  .track-panel .move-pair { display:none; }
}

/* A row's controls are one row of one size ----------------------------------
   The strip carries a number, a score and six buttons on every one of a
   hundred and fifty rows, so it is a notch down from a control that stands on
   its own: 34px against the page's 38px. The rank and the score read the same
   variable, so the three kinds of thing in the strip cannot drift apart — the
   rank used to be 2rem against the buttons' 38px, which is the mismatch that
   made the row look assembled rather than laid out. */
.track-choice-actions { --action-h:34px; }
.track-choice-lead { --action-h:34px; }

/* One row, opened on its own -------------------------------------------------
   The panel switch decides how the *list* reads; this is how one row reads.
   Pressing a row opens it to the full card and pressing it again closes it, so
   checking the code of the row under the cursor costs a click rather than a
   trip to the switch at the top of a hundred and fifty rows and back.

   An opened row is **exempted from the compact rules**, which is what makes it
   identical to the full reading rather than similar to it. This used to be a
   second set of rules that put back what compact takes away, and a copy of a
   layout is a layout that drifts: the opened row wrapped its title differently,
   kept the compact chip spacing, and sat at the compact row height, so «open
   one row» and «open every row» showed the same offering two ways. The
   exemption is written once, on the compact selector itself
   (`.track-choice:not(.is-open)`), so there is nothing to keep in step.

   The guard is the catalogue's own: everything in the row that is itself a
   control keeps its own press (`.catalog-row` in catalog.js uses the same
   list). The state is transient on purpose — what is remembered is the way the
   reader chose to read the list, not which row they last peeked into. */
.track-panel.is-compact .track-choice { cursor:pointer; }
.track-panel.is-compact .track-choice.is-open { cursor:default; }

/* ---- reading a long list two ways -------------------------------------- */
/* Compact is the default (see decision-workspace.js) and it is the compact
   reading that has to earn the switch: a version of the row that is only a
   little shorter than the full one is a control with nothing behind it.
   So compact is *one line* — on a wide screen the whole row, rank to remove
   button, sits on a single baseline and thirty of them fit in a screen.
     rank │ field · university · city │ group · fee · competition │ score │ ⋯
   The title is the elastic part and it truncates; the chips beside it are
   fixed. What compact drops is the directory half of the strip, the chips
   that carry `is-detail`: the code, the degree, the study mode, an ordinary
   capacity, the admission basis. What it keeps is what actually decides an
   order — which exam group may register the row, whether it costs money, how
   hard the seats are, and a capacity small enough to change the answer.

   Both readings are stated here rather than compact being the exception,
   because compact is what a reader gets.

   Every rule below reaches `.track-choice` and stops there. The panel also
   holds the windows that add to the list, and their cards are not this list's
   rows: scoped to the panel alone, «نمای فشرده» reached into «مرور گزینه‌های
   منطبق» and took the code, the capacity and the study mode off the cards a
   reader was choosing *from* — which is the one moment they need them. */
/* Compact differs from full in ONE thing: how much air a row is given. It had
   drifted into differing in four — the row's padding, the list's gap, the
   title's gap and the card's row-gap — which is why the two views read as two
   typefaces to somebody switching between them. They are not: every font-size,
   weight, colour and line-height is identical in both, measured. The two gaps
   that had no reason to differ now do not, and what is left is the vertical
   rhythm, which is what the switch is for. */
.track-panel.is-compact .track-choice:not(.is-open) { align-items:center; padding-block:.4rem; }
.track-panel.is-compact .track-choice:not(.is-open) .reason-chips,
.track-panel.is-compact .track-choice:not(.is-open) .offering-where,
.track-panel.is-compact .track-choice:not(.is-open) .offering-tag.is-detail,
.track-panel.is-compact .track-choice:not(.is-open) .competition.is-detail,
.track-panel.is-compact .track-choice:not(.is-open) .offering-tag-group.is-detail { display:none; }
/* The rank keeps the strip's own control height in both readings: it is one of
   the controls in that row, not a label beside them. */
/* The rank keeps the strip's own control height in both readings. */
.track-panel.is-compact .track-choices { gap:.4rem; }
.track-panel.is-compact .track-choice:not(.is-open) .offering-title .offering-at { display:inline; }
.track-panel.is-compact .track-choice:not(.is-open) .offering-title .offering-city { display:inline; }
/* The card is a row, not a stack: the title's parts and the chips beside them
   are one wrapping line, so a narrow screen breaks it wherever it runs out of
   room instead of spending a whole line on a single chip. The chips follow the
   words they qualify rather than being pushed to the far end of the line —
   `flex:0 1 auto` on both halves, so neither grows into the gap and the exam
   group cannot end up alone on the opposite side of the row from its name. */
.track-panel.is-compact .track-choice:not(.is-open) .offering-body {
  display:flex; align-items:center; flex-wrap:wrap;
  /* The row-gap is the card's own — the same .15rem the stacked reading uses —
     so a line that wraps in compact sits exactly where it sits in full. Only
     the column gap is new, because only this reading has columns. */
  gap:.15rem var(--chip-gap);
}
.track-panel.is-compact .track-choice:not(.is-open) .offering-meta { margin-block-start:0; }
/* No gap of its own: the title's parts are spaced the same however the card
   around them is laid out. */
.track-panel.is-compact .track-choice:not(.is-open) .offering-title { min-width:0; }
/* Below the width that fits the row on one line, the two halves of the card
   stop being boxes and their contents join the row's own wrap. Kept as boxes
   they could not share a line: the field name and the university fill the
   width, so the title box was the whole line and every chip after it started a
   new one — a five-word row spent four lines, one of them holding «تطابق ۹۱٪»
   and nothing else. `display:contents` puts the name, the university, the
   city, the score and the exam group into one sequence that breaks where it
   runs out of room, which is two lines instead of four. */
@media (max-width:1100px) {
  .track-panel.is-compact .track-choice:not(.is-open) .offering-title,
  .track-panel.is-compact .track-choice:not(.is-open) .offering-meta,
  .track-panel.is-compact .track-choice:not(.is-open) .offering-tags { display:contents; }
}
/* The sample on the way in is a narrow column beside the promise, not a
   full-width list, so it wraps at every width. Clipping it against the
   viewport measured the wrong box: on a ۱۵۰۰px screen the sample is ۵۳۰px
   wide, and the one-line rule below cut «دانشگاه شهید بهشتی» to «دانشگاه
   شهید …» and «تهران» to «ت…», which tells a first-time visitor nothing. */
/* Wide enough for one line, so on a desktop the whole row — rank, field,
   university, city, تطابق, the chips, and every control — sits on a single
   baseline and thirty rows fit in a screen. That is the point of the compact
   reading, and a version of the row that is merely a bit shorter than the full
   one is a switch with nothing behind it.

   The name of a رشته‌محل is longer than any row can be, so the elastic part is
   clipped at its end rather than wrapping the row to two. Nothing is lost: the
   row carries its full text in `data-choice-label`, the detail window says it
   in full, and the full reading is one click away.

   1100px, not 900: the row has a fixed cost before the names get any width —
   the rank, six controls, the score and three chips — and between 900 and 1100
   what is left over is not enough to clip *gracefully*. Measured on a real
   plan at 950px, «تهران» collapsed to nothing at all and «دانشگاه صنعتی شریف»
   to 41 of its 135 pixels, which is «دانشگاه ت…» — a row that has stopped
   saying where it is. A tablet gets the wrapping reading instead, which is two
   legible lines. */
@media (min-width:1101px) {
  .track-panel.is-compact .track-choice:not(.is-open) .offering-body { flex-wrap:nowrap; }
  .track-panel.is-compact .track-choice:not(.is-open) .offering-meta { flex:none; }
  /* The one elastic thing on the line, so the chips keep their size and the
     names give up theirs. */
  .track-panel.is-compact .track-choice:not(.is-open) .offering-title { flex:1 1 12rem; flex-wrap:nowrap; }
  .track-panel.is-compact .track-choice:not(.is-open) .offering-title>strong,
  .track-panel.is-compact .track-choice:not(.is-open) .offering-at,
  .track-panel.is-compact .track-choice:not(.is-open) .offering-place {
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* The field is what the row is, so it gives up its width last. */
  .track-panel.is-compact .track-choice:not(.is-open) .offering-title>strong { flex:0 1 auto; min-width:6rem; }
  .track-panel.is-compact .track-choice:not(.is-open) .offering-at { flex:0 2 auto; min-width:0; }
  .track-panel.is-compact .track-choice:not(.is-open) .offering-place { flex:0 3 auto; min-width:0; }
  .track-panel.is-compact .track-choice:not(.is-open) .offering-score { flex:none; }
}
/* «دارای شهریه» is a cost, and it was the quietest chip on the strip — grey on
   white, beside a «تحصیل رایگان» painted green. A reader scanning for what a
   place will cost them found the good news marked and the bad news unmarked:
   «موضوع نوبت دوم یا دارای شهریه باید مشخص‌تر و واضح تر باشد». The pair now
   reads as a pair, on the warm/cold axis this product already uses for «asks
   something of you» and «does not». It is not a warning — a fee is an ordinary
   fact about an ordinary course — so it takes the same weight as its opposite
   and not the alarm tint that `is-warning` carries. */
.offering-tag.is-tuition { border-color:#e6d2b4; background:#fdf4e6; color:#7d4a12; }
.offering-tag.is-warning { background:var(--warn-bg,#fdf0e3); color:var(--warn-ink,#8a4b08); }

/* «گزارش مشکل» — the way to say something here is wrong ----------------------
   A chip on the left edge, turned on its side: the shape FaraDars already uses
   for this, so a reader who has seen one has seen both. `left` is negative by
   the chip's own height because the rotation happens after layout — the box is
   laid out lying down and then stood up, so what hangs off the edge is the
   height, and pulling it back by that much sets it flush.

   Kept on a phone, unlike the reference, which hides it below its small
   breakpoint. A wrong رشته‌محل is at least as likely to be noticed on a phone,
   and 32px on the edge of a page costs nothing to a reader who is not using it. */
.feedback-tab {
  position:fixed; z-index:70;
  /* `left`, physically, not `inset-inline-start`: this page is RTL, where the
     inline start edge is the right one, and the tab belongs on the left.

     The offset and the corners both follow from *where the box is rotated
     about*. Turning it about its centre made the flush position depend on the
     chip's own text width — the left edge landed wherever `(width/2 − height/2)`
     happened to put it, which is the ۵px gap that was there. Turned about its
     bottom-left corner instead, the box sweeps left by exactly its own height,
     so setting `left` to that height puts it flush at zero whatever the text
     says. `--tab-h` is that height, written once and used by both. */
  --tab-h:2rem;
  left:var(--tab-h); bottom:9rem;
  display:inline-flex; align-items:center; gap:.35rem;
  height:var(--tab-h); padding:0 .6rem;
  transform:rotate(-90deg); transform-origin:0 100%;
  /* A quarter turn anticlockwise sends the box's top edge to the left of the
     screen and its bottom edge to the right, so the corners that must be
     square are the *logical top* ones and the rounded pair is the bottom. It
     read the other way round: rounded against the screen edge, square where
     the chip meets the page. */
  border:1px solid var(--brand-line); border-radius:0 0 8px 8px;
  background:var(--brand-soft); color:var(--brand-dark);
  font:inherit; font-size:.76rem; font-weight:700; line-height:1;
  white-space:nowrap; cursor:pointer;
}
.feedback-tab:hover { background:var(--brand-tint); }
.feedback-tab .action-icon { width:14px; height:14px; flex:none; }
/* The focus ring belongs to the chip as the reader sees it, so it is drawn
   inside the rotated box rather than around the un-rotated one. */
.feedback-tab:focus-visible { outline:2px solid var(--focus); outline-offset:-3px; }
@media print { .feedback-tab { display:none !important; } }

.feedback-dialog { width:min(30rem,calc(100% - 2rem)); }
/* Two ways in, one shape: the same bordered row the rest of the product uses
   for «choose one of these», with the glyph carrying which is which. */
.feedback-choices { display:grid; gap:.6rem; }
.feedback-choice {
  display:flex; align-items:flex-start; gap:.7rem; width:100%;
  border:1px solid var(--line); border-radius:var(--radius-surface);
  background:var(--surface); padding:.85rem 1rem;
  color:inherit; font:inherit; text-align:start; cursor:pointer;
}
.feedback-choice:hover { border-color:var(--brand); background:var(--brand-soft); }
.feedback-choice .action-icon { width:18px; height:18px; flex:none; margin-top:.15em; color:var(--brand-dark); }
.feedback-choice strong { display:block; color:var(--ink); font-size:.98rem; }
.feedback-choice small { display:block; margin-top:.15rem; color:var(--muted); font-size:.85rem; line-height:1.7; }

/* The one field in the window, shaped like every other field in the product.
   Its border, background, padding and corners are the shared ones — the corner
   was the odd one out, because the radius every other box has was written into
   each component that has a box rather than into the field itself, and this
   field belongs to no such component. It sat square-cornered inside a rounded
   window, beside rounded buttons, under two rounded choice rows. */
.feedback-form { display:grid; gap:.35rem; }
.feedback-form label { color:var(--ink); font-weight:700; }
.feedback-form textarea { width:100%; min-height:8.5rem; line-height:1.9; resize:vertical; }
/* The counter belongs to the box above it, not to the row of buttons below. */
.feedback-form .character-count { margin-top:0; }
.feedback-actions { display:flex; justify-content:flex-end; gap:.5rem; margin-top:.6rem; }
