@import url("css/tokens.css");
@import url("css/sitenav.css");   /* the persistent left rail — shared with pm/*.html */
.icon{width:1em;height:1em;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;fill:none;vertical-align:-0.125em;flex-shrink:0}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{font-family:var(--fb);background:var(--bg);color:var(--text);line-height:1.6;font-size:15px}
/* -webkit-font-smoothing:antialiased used to be unconditional on body. On macOS that
   turns OFF subpixel rendering, which thins every stroke and was part of why the site
   read as hard to focus on. Light mode uses the platform default (subpixel), which
   renders dark-on-light heavier. Dark mode keeps antialiased: there the polarity is
   inverted and light-on-dark text blooms without it. Same split in admin.css.
   This split was introduced when --fb was Grift, a light-stemmed geometric sans; it
   was KEPT through Inter even though `antialiased` is the more common convention for
   Inter on white, because the reason to prefer subpixel is that it lays down more ink
   and the point of that pass was legibility, not fashion. As of 2026-09-26 --fb is
   Visby CF, which IS a light-stemmed geometric sans — so the rule is back to the case
   it was written for, and the argument for keeping it is now the original one. */
body.dark{-webkit-font-smoothing:antialiased}

/* ── BURGER ── */
.burger{width:34px;height:34px;border-radius:50%;border:1px solid var(--b2);background:var(--bg1);cursor:pointer;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:5px;padding:0;flex-shrink:0;transition:background var(--d1)}
.burger:hover{background:var(--bg2)}
.burger span{display:block;width:18px;height:1.5px;background:var(--text);border-radius:2px;transition:transform var(--d2),opacity var(--d2)}
.burger.open span:nth-child(1){transform:translateY(6.5px) rotate(45deg)}
.burger.open span:nth-child(2){opacity:0}
.burger.open span:nth-child(3){transform:translateY(-6.5px) rotate(-45deg)}
/* The burger only appears below 920px, where the rail becomes a drawer — and
   the collapse control swaps out with it, since there's nothing to collapse
   once the rail is off-canvas. (The pm pages' .rail-toggle stays: siteNav.js
   makes that one the drawer toggle at this width.) */
.burger{display:none}
@media(max-width:920px){
  /* 44px from the moment it appears, not only below 640: this is the drawer's
     only trigger, and the width at which the rail goes off-canvas is also the
     width at which the pointer is likely to be a thumb. */
  .burger{display:flex;width:44px;height:44px}
  .search-wrap #rail-toggle{display:none}
}

/* ── CONTENT WRAPPER ── */
/* A flex column holding exactly three children: the sticky top bar, #tiers (the
   full-screen Overview stage) and .main (every other section). Column + a
   viewport min-height is what lets #tiers fill the screen *under* the bar
   without anyone hardcoding that bar's height — it measures 53px on desktop but
   55 at 768px and 65 once the burger takes over, so the old
   `min-height:calc(100vh - 120px)` on the stage was wrong at every width.
   svh rather than vh: on mobile vh is the LARGE viewport, so the stage's bottom
   would sit under the browser's own UI. */
.content-wrap{width:100%;min-width:0;display:flex;flex-direction:column;min-height:100vh;min-height:100svh}

/* ── HERO ── */
/* Left padding used to leave room for the old fixed nav trigger; the rail now
   occupies that space, so content starts at the normal gutter. */
.hero{padding:56px 40px 48px;max-width:1440px;margin:0 auto}
.hk{font-family:var(--fb);font-size:12px;color:var(--s-text);letter-spacing:var(--tr-lg);text-transform:uppercase;margin-bottom:22px;display:flex;align-items:center;gap:12px}
.hk::before{content:'';width:26px;height:1px;background:var(--s)}
.ht-guj{font-family:var(--fd);font-size:clamp(56px,10vw,120px);font-weight:600;color:var(--text);line-height:.96;letter-spacing:-.01em;margin-bottom:6px}
.ht-en{font-family:var(--fd);font-size:clamp(20px,3.2vw,36px);font-weight:500;color:var(--muted);letter-spacing:0;margin-bottom:26px}
.hm{font-size:16px;color:var(--muted);max-width:640px;line-height:1.8;margin-bottom:12px}
.hm em{color:var(--text);font-style:normal;font-weight:500}
.hs{font-size:13px;color:var(--muted);max-width:560px;line-height:1.7;margin-bottom:32px;opacity:.85}
.hst{display:flex;gap:20px;flex-wrap:wrap;padding-top:24px;border-top:1px solid var(--border)}
.hstat{font-size:14px;color:var(--muted);display:flex;align-items:center;gap:8px}
.hsd{width:5px;height:5px;border-radius:50%;background:var(--s);flex-shrink:0}
.hnums{display:flex;gap:clamp(24px,4vw,44px);flex-wrap:wrap;margin:32px 0 4px}
.hn{display:flex;flex-direction:column;gap:3px}
.hn-n{font-family:var(--fd);font-size:clamp(30px,4vw,42px);font-weight:600;color:var(--s-text);line-height:1;font-variant-numeric:tabular-nums}
.hn-l{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--dim)}

/* ── TIER STRIP ── */
.ts{background:var(--bg1);border-top:1px solid var(--border);border-bottom:1px solid var(--border);display:flex;overflow-x:auto;padding-left:32px}
.tier{padding:15px 22px;border-right:1px solid var(--border);flex-shrink:0;min-width:180px}
.tier:last-child{border-right:none}
.tier-num{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--muted);margin-bottom:3px;font-variant-numeric:tabular-nums}
.tier-name{font-family:var(--fd);font-size:14px;color:var(--text);line-height:1.2;margin-bottom:2px}
.tier-age{font-size:14px;color:var(--muted);margin-bottom:5px}
.tier-msg{font-size:14px;color:var(--muted);font-family:var(--fd);line-height:1.4}

/* ── MAIN ── */
/* padding-top is --page-gap, not 0: every section in here opens with a title
   (.nc-section-intro band) and .search-wrap is sticky at top:0 directly above,
   so at 0 the title sat against the bar's border. #tiers is deliberately NOT in
   here — it's the full-bleed vakya stage, which carries its own 64px. */
.main{width:100%;min-width:0;max-width:1440px;margin:0 auto;padding:var(--page-gap) 40px 80px}
/* Archive is a working tracker rather than a reading page. Its rows need the
   available canvas, while the intro and expanded copy carry their own readable
   measures below. Keeping this route-specific avoids stretching every guide
   section just to fix one dense operational surface. */
body.view-archive .main{max-width:none}
/* Overview is the one view whose section sits outside .main, so while it's the
   active view every child of .main is display:none and nothing is left but
   .main's own 32/80px padding — 112px of bare page hanging off the bottom of a
   full-screen section. script.js's showView() sets the class. */
body.view-overview .main{display:none}
/* ── SECTION INTRO — the guide's one page-title component ──
   REPLACES .sec-eye/.sec-h/.sec-d-as-a-header AND .asset-hdr/.asset-ico/
   .asset-title/.asset-sub, which were two systems for the SAME rank: a 20px
   title behind a 40px tinted chip on Templates and File Drop, a
   clamp(26,4vw,34) title with a tracked eyebrow on the Asset Map and Archive.
   All fourteen section files now open with the same markup:

     <header class="nc-pagehead nc-section-intro">
       <span class="nc-pagehead-eye">Section 04</span>
       <h2   class="nc-pagehead-title">File Drop</h2>
       <p    class="nc-pagehead-sub">…</p>
     </header>

   .nc-pagehead is the shared component (css/primitives.css) that PM and admin
   also use — so a guide section and a board now agree about what a page title
   is. .nc-section-intro is the guide's variant of it: research takeaway #3
   (docs/redesign/01-design-research.md), an inverted band carrying the section
   number and title at display scale that then hands off to the light content.
   That is why this is a modifier and not a second component — one markup shape,
   one set of child class names, one place to change the rank.

   The ordinal is substituted by build.js from SECTION_IDS' order, never written
   into a section file: ten of the fourteen are parked, so a hand-written number
   is wrong the moment one moves. */
.nc-section-intro{position:relative;overflow:hidden;
  padding:var(--sp-48) var(--sp-40);margin:0 0 var(--sp-32);
  border:0;border-radius:var(--r-xl);
  background:var(--surface-invert);color:var(--on-invert)}
/* The login canvas's own bloom, reused. --s2 IS rgba(190,58,52,.16), so this
   needs no hex of its own and follows the accent if it ever moves. */
.nc-section-intro::after{content:"";position:absolute;inset:-40% -10% auto auto;
  width:60%;aspect-ratio:1;pointer-events:none;
  background:radial-gradient(circle,var(--s2),transparent 62%)}
/* Everything below sits above that bloom. */
.nc-section-intro > *{position:relative;z-index:1}
/* The ONE place the site-wide ::selection in primitives.css is wrong, and for
   the same reason --on-invert exists: that rule pins selected ink to --text,
   which in LIGHT mode is #0a0a0a — near-black on the tinted near-black band,
   1.39:1. --on-invert instead gives 13.07:1 light / 10.45:1 dark. Both forms
   are listed because the band's own text nodes and its children both select. */
.nc-section-intro::selection,.nc-section-intro ::selection{color:var(--on-invert)}
/* ⚠️ BRAND LAW: orange as TEXT is legal on dark only, and Dharma is 7.55:1 on
   near-black (AAA). --s, NOT --s-text — that is the darkened value that exists
   for LIGHT fields and is unreadable here. */
.nc-section-intro .nc-pagehead-eye{color:var(--s);font-weight:600;letter-spacing:var(--tr-lg);
  font-variant-numeric:lining-nums tabular-nums}
/* The one display-scale title on the guide, so it is the one that strands a word
   on its own line. Measured: "Akshardham Production Asset Map" broke 21ch/3ch at
   1024px and "Archival Footage & Photography Request List" broke 30ch/12ch at
   1440px and 18/19/4 at 390px; balance makes those 15/9, 18/24 and 16/13/12,
   with the line COUNT unchanged in every case, so nothing below it moves. */
.nc-section-intro .nc-pagehead-title{font-size:clamp(38px,5.5vw,68px);line-height:1;
  letter-spacing:-.01em;color:var(--on-invert);margin:var(--sp-8) 0 var(--sp-6);
  text-wrap:balance}
/* Opacity rather than a second colour: the band is dark in BOTH modes but its
   ink token is not the same value, so one rgba() would be right in only one of
   them (that is the trap --on-invert exists to close). */
.nc-section-intro .nc-pagehead-sub{font-size:var(--fs-lead);line-height:1.6;
  color:var(--on-invert);opacity:.74;max-width:58ch}
/* The one link allowed on the band (File Drop → Review queue). A pill, like every
   other button on the site, in the accent that is legal on dark. */
.nc-section-intro-link{display:inline-flex;align-items:center;gap:var(--sp-8);align-self:flex-start;
  min-height:44px;margin-top:var(--sp-8);padding:var(--sp-8) var(--sp-16);
  border:1px solid var(--s);border-radius:var(--r-pill);
  font-family:var(--fb);font-size:var(--fs-label);font-weight:600;letter-spacing:var(--tr-md);
  text-transform:uppercase;color:var(--s);text-decoration:none;
  transition:background var(--d1) var(--ease-out),color var(--d1) var(--ease-out),
             transform var(--spring-dur-1) var(--spring-1)}
.nc-section-intro-link .icon{width:14px;height:14px}
.nc-section-intro-link:hover{background:var(--s);color:var(--on-accent);transform:translateY(-1px)}
.nc-section-intro-link:active{transform:translateY(0) scale(.97)}
@media(max-width:640px){
  .nc-section-intro{padding:var(--sp-32) var(--sp-20);margin-bottom:var(--sp-24)}
}
/* The long descriptive paragraph that stays in the LIGHT content, under the
   band — the coverage note on the Asset Map, the sourcing note on Archive. It
   is no longer part of the header, so it reads at lead size rather than as
   caption-weight furniture under a title.

   pretty and NOT balance: at 70ch this sets 5 lines at 1440px and 10 at 390px,
   well past the 2-4 line window balance is for (Chrome declines to balance the
   10-line case at all). Measured on the Archive copy at 390px: the last line
   goes 8ch -> 11ch. It is a no-op on the wider breaks, which is what an orphan
   guard is supposed to be. */
.sec-d{font-size:var(--fs-lead);color:var(--muted);max-width:70ch;line-height:1.75;margin-bottom:var(--sp-24);text-wrap:pretty}

/* ── GRID RHYTHM ──
   Eight auto-fill card grids ran at eight arbitrary minima — 320/320/300/280/
   280/270/255/196px — all at gap:10px. Nothing chose those numbers, so every
   card carried the same weight and no two sections agreed on a column count at
   the same width. Three sizes now, each four steps of the --sp-* scale, and one
   gap:

     --grid-lg  320px  (--sp-80 x 4)  long-form cards: tools, programs, workflow
     --grid-md  256px  (--sp-64 x 4)  short cards: phases, skills, vakyas, crunch
     --grid-sm  192px  (--sp-48 x 4)  asset tiles, where the preview is the point

   The gap goes 10px -> --sp-16, which is where .tpl-tiles — the one grid already
   on the scale — already was.
   Declared early on purpose: every per-section override further down this file
   (#assetmap's single column, the phone block, the 600px tile step) is later and
   still wins on source order. */
:root{
  --grid-lg:calc(var(--sp-80) * 4);
  --grid-md:calc(var(--sp-64) * 4);
  --grid-sm:calc(var(--sp-48) * 4);
}
.tg,.prog-grid,.wf-grid,.vakya-grid,.rm-grid,.sk-grid,.cr-grid,.tpl-tiles{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--grid-min),1fr));gap:var(--sp-16)}
.tg,.prog-grid,.wf-grid{--grid-min:var(--grid-lg)}
.vakya-grid,.rm-grid,.sk-grid,.cr-grid{--grid-min:var(--grid-md)}
.tpl-tiles{--grid-min:var(--grid-sm)}
/* scroll-margin-top clears the sticky bar, not just a token of air. In-app nav
   goes through goTo(), which scrollTo(0,0)s, but a deep link into a long
   section (pm/*.html's rail points at ../index.html#templates, #uploads,
   #archive) is a real anchor scroll — and .search-wrap is in flow at top:0, so
   the old 20px parked the section's title *behind* the bar. 53px is that bar. */
.asset-sec{margin-bottom:64px;scroll-margin-top:calc(53px + var(--page-gap))}
/* Every section starts hidden, until script.js's applyRoute() has picked one and
   marked <html> routed. Without this the sections are only ever hidden by
   showView()'s inline style, which runs on DOMContentLoaded — so the browser
   first paints the WHOLE guide stacked (Overview + asset map + Templates +
   Archive + File Drop, ~115KB of it) and then collapses it to one.
   The flash was the visible half. The worse half was silent: a deep link
   (#archive, which pm/*.html's rail and any bookmark use) is an anchor scroll
   the browser performs against that un-collapsed document, so it scrolled to
   where #archive sat among all five sections — then showView() removed the four
   above it, the document shrank, and the scroll offset stayed, parking the
   visitor well past the section's own summary header. Hidden up front, the
   anchor has nowhere to scroll and applyRoute() lands at the top.
   Keyed off <html> rather than a body class so it applies from the first byte of
   CSS, before <body> exists. */
html:not(.nc-routed) #tiers,
html:not(.nc-routed) .asset-sec{display:none}
/* .asset-hdr / .asset-ico / .asset-title / .asset-sub were HERE. Deleted, not
   parked: all fourteen section files now open with .nc-pagehead.nc-section-intro
   above, including the ten in build.js's HIDDEN_SECTION_IDS, so nothing on disk
   carries these classes any more. (.asset-sec is a different thing and stays —
   it is the section wrapper the router shows and hides.) */

/* Templates — the Box branding library, rendered by js/templates.js. The empty
   block doubles as the loading and error state. */
.tpl-empty{border:1px dashed var(--b2);border-radius:var(--rl);padding:40px 28px;text-align:center;background:var(--bg2)}
.tpl-empty-title{font-family:var(--fb);font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--dim);margin-bottom:10px}
/* .nc-loader is inline-block, so in this centred box it needs to be its own
   block to sit above the eyebrow rather than beside it. */
.tpl-empty-spinner{display:block;margin:0 auto 16px}
.tpl-empty-body{font-size:14px;line-height:1.65;color:var(--muted);max-width:46ch;margin:0 auto}
.tpl-empty .tpl-dl{margin-top:16px}

.tpl-files{list-style:none;border:1px solid var(--border);border-radius:var(--rl);overflow:hidden;background:var(--bg1)}
.tpl-file{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:12px 16px;transition:background var(--d1)}
.tpl-file+.tpl-file{border-top:1px solid var(--border)}
.tpl-file:hover{background:var(--bg2)}
.tpl-file-main{min-width:0}
.tpl-file-name{font-size:14px;color:var(--text);overflow-wrap:anywhere}
.tpl-file-meta{font-family:var(--fb);font-size:11px;color:var(--dim);margin-top:3px;font-variant-numeric:tabular-nums}
.tpl-file-status{font-size:12px;color:var(--red);margin-top:4px}
.tpl-dl{display:inline-flex;align-items:center;justify-content:center;padding:6px 14px;border:1px solid var(--b2);border-radius:var(--r-pill);background:var(--bg1);color:var(--text);cursor:pointer;font-family:var(--fb);font-size:12px;line-height:1.6;flex-shrink:0;transition:background var(--d1),border-color var(--d1)}
.tpl-dl:hover{background:var(--bg2);border-color:var(--text)}
.tpl-dl[disabled]{opacity:.5;cursor:not-allowed}
.tpl-note{font-size:12px;color:var(--dim);margin-top:14px}

/* ---- toolbar: breadcrumb + filter + view switch --------------------------
   One row above the library. The breadcrumb is the whole navigation model —
   there is no separate back button, because Box's own crumb is the pattern
   people already know from the folder they upload to. */
.tpl-bar{display:flex;align-items:center;gap:var(--sp-12);flex-wrap:wrap;margin-bottom:var(--sp-20)}
.tpl-crumbs{display:flex;align-items:center;gap:var(--sp-2);flex:1;min-width:0;flex-wrap:wrap}
.tpl-crumb{display:inline-flex;align-items:center;justify-content:center;font-family:var(--fb);font-size:var(--fs-body-sm);line-height:1.6;color:var(--muted);background:none;border:0;
  padding:var(--sp-4) var(--sp-10);border-radius:var(--r-pill);cursor:pointer;transition:background var(--d1),color var(--d1)}
.tpl-crumb:hover{background:var(--tint);color:var(--text)}
.tpl-crumb[aria-current="page"]{color:var(--text);font-weight:500;cursor:default}
.tpl-crumb[aria-current="page"]:hover{background:none}
.tpl-crumb-sep{color:var(--dim);font-size:var(--fs-caption);user-select:none}
/* Filter is a pill because search fields are pills site-wide (#89); every other
   field on the site stays at 8px. */
.tpl-filter{border-radius:var(--r-pill);padding:var(--sp-8) var(--sp-16);width:min(220px,42vw)}
.tpl-views{display:inline-flex;gap:2px;padding:2px;border:1px solid var(--b2);border-radius:var(--r-pill);flex-shrink:0}
.tpl-view{display:grid;place-items:center;width:34px;height:26px;border:0;background:none;border-radius:var(--r-pill);
  color:var(--dim);cursor:pointer;transition:background var(--d1),color var(--d1)}
.tpl-view .icon{width:15px;height:15px;stroke-width:1.6}
.tpl-view:hover{color:var(--text)}
.tpl-view[aria-pressed="true"]{background:var(--text);color:var(--bg)}

/* ---- folder cards (grid view) -------------------------------------------
   Top level of Branding/ = a card per folder. The card's face is a 2x2 mosaic
   of what is actually inside it, which is the cheap version of the ask: you can
   tell the Logos folder from the Lower Thirds folder without opening either.
   Fewer than four previewable children collapses the mosaic (--one / --two), so
   a folder holding one logo shows that logo big rather than one quarter-tile
   and three empty cells.

   Folder cards share the one .tpl-tiles grid with the file tiles rather than
   sitting in a shelf of their own: two grids meant the files always broke to a
   new row, even with five empty columns beside the folders. */
.tpl-folder{position:relative;display:flex;flex-direction:column;overflow:hidden;
  background:var(--bg1);border:1px solid var(--border);border-radius:var(--rl);
  box-shadow:var(--elev-card);
  transition:transform var(--d1) var(--ease-out),box-shadow var(--d2) var(--ease-out),border-color var(--d1)}
.tpl-folder:hover{transform:translateY(-2px);border-color:var(--border-raised);box-shadow:var(--elev-card-hover)}
.tpl-folder:focus-within{border-color:var(--border-raised)}
/* flex-grow rather than flex:1 on both of these, twice deliberately: flex:1 also
   sets flex-basis:0, which would drop the mosaic out of the card's intrinsic
   height and collapse an unstretched card to its name band alone. Growing is
   what makes a folder card share a grid row with the taller file tiles and still
   reach the same bottom edge — the mosaic takes the slack, the name band stays
   pinned to the foot of the card. */
.tpl-folder-open{flex-grow:1;display:flex;flex-direction:column;
  width:100%;text-align:left;padding:0;border:0;background:none;cursor:pointer}
.tpl-folder-open:active{transform:scale(.997);transition-timing-function:var(--spring-1)}
/* 26px rather than the 30px used on tiles: the footer is a 40px band and a 30px
   circle inside it leaves no breathing room above or below. It also buys the gap
   between the file count and the button — at 30px they sat 4px apart.
   Qualified with .tpl-icon-btn deliberately: the base .tpl-icon-btn rule below
   sets 30px at the same specificity, so a bare .tpl-folder-dl loses to it on
   source order and this override would silently do nothing. Scoped to the card
   because list view reuses the class on a button that sits in the row's flow. */
.tpl-folder > .tpl-icon-btn.tpl-folder-dl{position:absolute;right:var(--sp-8);bottom:var(--sp-6);
  width:26px;height:26px}
.tpl-folder > .tpl-icon-btn.tpl-folder-dl .icon{width:14px;height:14px}
/* 4:3, matching .tpl-thumb, so a row of folders and a row of tiles stand on the
   same rhythm. The ratio is the mosaic's natural height, not a cap: it grows
   with the card when the row is taller (see .tpl-folder-open above). */
.tpl-mosaic{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;gap:1px;
  flex-grow:1;aspect-ratio:4/3;background:var(--border)}
.tpl-mosaic--one{grid-template-columns:1fr;grid-template-rows:1fr}
.tpl-mosaic--two{grid-template-rows:1fr}
/* The image is absolutely positioned, which is what makes it stay inside the
   cell. A percentage height on an in-flow child here is cyclic — the row is 1fr
   inside an aspect-ratio box, so its height isn't definite when the child is
   measured — and both `height:100%` and `max-height:100%` resolve to nothing:
   a 112x147 icon measured 121px tall in a 60px cell and got quietly sliced in
   half by overflow:hidden. Landscape logos were unaffected, which is why a shelf
   of wide lockups looked fine. inset:0 against the positioned cell is a definite
   containing block, so contain has real bounds to letterbox inside. */
.tpl-mosaic-cell{position:relative;background:var(--bg2);display:grid;place-items:center;overflow:hidden;min-width:0}
.tpl-mosaic-cell img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;padding:9px}
.tpl-mosaic--one .tpl-mosaic-cell img{padding:16px}
.tpl-mosaic-cell .icon{width:18px;height:18px;color:var(--dim);stroke-width:1.4}
/* padding-right reserves the zip button's lane, so a long folder name runs into
   its ellipsis instead of under the icon. */
.tpl-folder-body{display:flex;align-items:center;gap:var(--sp-8);padding:var(--sp-10) var(--sp-12);
  padding-right:42px;border-top:1px solid var(--border)}
.tpl-folder-body .icon{width:15px;height:15px;color:var(--dim);flex-shrink:0;stroke-width:1.6}
.tpl-folder-name{font-family:var(--fb);font-size:var(--fs-body);color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tpl-folder-count{margin-left:auto;flex-shrink:0;font-family:var(--fb);font-size:var(--fs-micro);
  letter-spacing:var(--tr-xs);color:var(--dim);font-variant-numeric:tabular-nums}

/* ---- file tiles (grid view) ---------------------------------------------
   The preview itself. Fixed 4:3 well, asset contained inside it — never
   cropped, because a cropped logo is a misleading preview.

   The grid holds the folder cards too, folders first: one flow, so a folder and
   a file are on the same line whenever the line has room for both. */
.tpl-tile{position:relative;display:flex;flex-direction:column;overflow:hidden;
  background:var(--bg1);border:1px solid var(--border);border-radius:var(--rl);
  box-shadow:var(--elev-card);
  transition:transform var(--d1) var(--ease-out),box-shadow var(--d2) var(--ease-out),border-color var(--d1)}
.tpl-tile:hover,.tpl-tile:focus-within{transform:translateY(-2px);border-color:var(--border-raised);box-shadow:var(--elev-card-hover)}
.tpl-thumb{position:relative;display:grid;place-items:center;aspect-ratio:4/3;overflow:hidden;
  width:100%;padding:0;border:0;border-bottom:1px solid var(--border);background:var(--bg2);cursor:zoom-in}
/* Nothing to enlarge — a font, an archive, or a type Box declined to render. */
.tpl-thumb[data-nopreview]{cursor:default}
/* Absolutely positioned for the same reason as .tpl-mosaic-cell img above: the
   well's height comes from aspect-ratio, so a percentage clamp on an in-flow
   child is cyclic and does not apply. A portrait asset — an icon mark, a vertical
   lockup — measured 179px tall in a 146px well and lost its top and bottom to
   overflow:hidden, which is precisely the "cropped logo is a misleading preview"
   this well exists to avoid. */
.tpl-thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;padding:var(--sp-16);display:block}
/* Transparency, made visible. A PNG logo on a plain grey well looks like it has
   a grey background; the checker is how every design tool says "this is alpha",
   and it matters here because the #1 asset in this library is a cutout logo. */
.tpl-thumb--alpha{
  background-image:linear-gradient(45deg,var(--bg3) 25%,transparent 25%,transparent 75%,var(--bg3) 75%),
                   linear-gradient(45deg,var(--bg3) 25%,transparent 25%,transparent 75%,var(--bg3) 75%);
  background-size:14px 14px;background-position:0 0,7px 7px}
/* A white-ink logo is invisible on a light well, so its preview carries its own
   dark backing regardless of the page's mode — same reason the brand guidelines
   show the reversed lockup on black. */
.tpl-thumb--dark{background:#2d2926}
/* No thumbnail available (font, archive, audio, 3D, or Box has not generated
   one). Typographic, not a broken-image icon: it states the format. */
.tpl-fallback{display:flex;flex-direction:column;align-items:center;gap:var(--sp-8);color:var(--dim)}
.tpl-fallback .icon{width:24px;height:24px;stroke-width:1.25}
.tpl-fallback-ext{font-family:var(--fb);font-size:var(--fs-micro);letter-spacing:var(--tr-md);text-transform:uppercase}
.tpl-thumb .nc-skeleton{position:absolute;inset:0;height:auto;border-radius:0}
/* Format chip, top-left of the well. Reads at a glance which of the four PNGs
   is the SVG. z-index because the loading skeleton is a later-painted positioned
   sibling and would otherwise cover it. */
.tpl-kind{position:absolute;z-index:1;top:var(--sp-8);left:var(--sp-8);font-family:var(--fb);font-size:10px;
  letter-spacing:var(--tr-sm);text-transform:uppercase;color:var(--muted);background:var(--bg-glass);
  border:1px solid var(--border);border-radius:var(--r-pill);padding:2px var(--sp-8);pointer-events:none}
@supports (backdrop-filter:blur(1px)){.tpl-kind{backdrop-filter:blur(var(--blur-sm))}}
.tpl-tile-body{padding:var(--sp-10) var(--sp-12) var(--sp-12);min-width:0}
/* Clamped to two lines, and RESERVED at two lines — min-height matching the clamp
   is what keeps the Download buttons on one line across the row. Without it a
   one-line filename pulls its button up and the grid reads ragged. */
.tpl-tile-name{font-family:var(--fb);font-size:var(--fs-body-sm);color:var(--text);line-height:1.35;
  min-height:2.7em;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* Download lives BELOW the preview, never over it: a frosted pill on top of a
   contained logo covers the artwork it is advertising, and on a tall asset it
   lands squarely on the subject. A preview you have to look around is not a
   preview. So the tile splits cleanly — the well is for the asset, the body is
   for the words and the action. Always visible, not hover-revealed: downloading
   is the entire purpose of this page, and hover-gating the primary action hides
   it from every touch device. */
.tpl-tile-foot{display:flex;align-items:center;gap:var(--sp-8);margin-top:var(--sp-6)}
.tpl-tile-meta{font-family:var(--fb);font-size:var(--fs-micro);color:var(--dim);
  font-variant-numeric:tabular-nums;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---- icon buttons -------------------------------------------------------
   Circular because the site's buttons are stadiums and a stadium at 1:1 is a
   circle — the same shape language, not a new one. The cost of icon-only is
   discoverability, and it is paid for explicitly: every one carries an
   aria-label naming the FILE (not just "Download", so a screen reader in a grid
   of thirty knows which), plus a title so a pointer user gets the same words on
   hover. 30px, i.e. under the 44px touch minimum, which is why the row it sits
   in has 12px of padding around it — the tap target is the padded box. */
.tpl-icon-btn{display:inline-grid;place-items:center;width:30px;height:30px;flex-shrink:0;
  border:1px solid var(--b2);border-radius:50%;background:var(--bg1);color:var(--muted);cursor:pointer;
  transition:background var(--d1),color var(--d1),border-color var(--d1),transform var(--d1) var(--ease-out)}
.tpl-icon-btn .icon{width:15px;height:15px;stroke-width:1.7}
.tpl-icon-btn:hover{color:var(--text);border-color:var(--text);transform:translateY(-1px)}
.tpl-icon-btn:active{transform:translateY(0) scale(.94)}
.tpl-icon-btn[disabled]{opacity:.45;cursor:not-allowed;transform:none}
/* the busy state, since a Box download URL is minted per click and takes a moment */
.tpl-icon-btn[data-busy] .icon{animation:tpl-pulse 1s var(--ease-in-out) infinite}
@keyframes tpl-pulse{50%{opacity:.35}}

/* ---- selection ----------------------------------------------------------
   Multi-select, so a folder can go down in one action instead of eleven. The
   checkbox is ALWAYS rendered rather than revealed on hover: hover-reveal is the
   familiar pattern (Drive, Box's own web app) but it hides the entire feature
   from touch, and this library is browsed on phones. At rest it is a hairline
   square carrying no fill, quiet enough to sit on thirty tiles; it gains
   contrast when hovered and inverts when checked.

   The unchecked box is TRANSPARENT, with a dark border and a white outer ring.
   A frosted light fill was tried first and on the reversed-logo tile — whose
   well is near-black — it read as already ticked. The two-tone outline is the
   standard trick for a control that must sit on unpredictable image content:
   whichever way the pixels behind it go, one of the two strokes has contrast.
   Hardcoded rather than tokenised on purpose — what it needs contrast against is
   the ARTWORK, and the artwork does not flip with the colour mode. */
.tpl-pick{position:absolute;z-index:2;top:var(--sp-8);right:var(--sp-8);width:20px;height:20px;
  display:grid;place-items:center;border:1px solid rgba(0,0,0,.45);border-radius:var(--r-xs);
  background:transparent;color:transparent;cursor:pointer;box-shadow:0 0 0 1px rgba(255,255,255,.8);
  transition:background var(--d1),color var(--d1),border-color var(--d1),box-shadow var(--d1)}
.tpl-pick .icon{width:13px;height:13px}
.tpl-pick:hover{background:rgba(229,222,207,.92);border-color:#2d2926}
.tpl-pick[aria-checked="true"]{background:#2d2926;border-color:#2d2926;color:#e5decf;
  box-shadow:0 0 0 1px rgba(255,255,255,.8)}
/* A picked tile reads as picked from across the grid, not only at its corner. */
.tpl-tile[data-picked]{border-color:var(--text);box-shadow:var(--elev-card-hover)}
/* In a row the checkbox is in the flow at the left edge, not floating in a
   corner — a list already has a column for it, which is why list view is the
   better surface for picking twenty files. */
.tpl-pick--row{position:static;flex-shrink:0}
.tpl-file[data-picked]{background:var(--tint)}

/* The toolbar's left half swaps from breadcrumb to selection count. No floating
   bar, no new furniture — the row that told you where you are now tells you what
   you have, which is the more useful thing while you are choosing. */
.tpl-selbar{display:none;align-items:center;gap:var(--sp-10);flex:1;min-width:0}
.tpl-bar[data-selecting] .tpl-crumbs{display:none}
.tpl-bar[data-selecting] .tpl-selbar{display:flex}
.tpl-selcount{font-family:var(--fb);font-size:var(--fs-body-sm);font-weight:500;color:var(--text);
  font-variant-numeric:tabular-nums}
.tpl-sellink{font-family:var(--fb);font-size:var(--fs-caption);color:var(--s-text);background:none;border:0;
  padding:var(--sp-4) var(--sp-6);border-radius:var(--r-pill);cursor:pointer}
.tpl-sellink:hover{background:var(--tint)}
/* The zip button keeps its words: it is the one button here where the count IS
   the label, and no icon can carry "3 files". */
.tpl-zip{display:inline-flex;align-items:center;gap:var(--sp-6);flex-shrink:0}
.tpl-zip .icon{width:15px;height:15px}
/* An explicit `display` beats the UA's [hidden]{display:none}, so both of these
   need it spelled out or they stay on screen when JS hides them. */
.tpl-zip[hidden],.tpl-icon-btn[hidden]{display:none}
/* Where an archive reports a partial or failed result. On the toolbar row, not
   inside .tpl-selbar — that is display:none whenever nothing is picked, which is
   exactly when the folder-wide archive button is the one on offer, so its
   failures would have been invisible. flex-basis gives it its own line under the
   controls instead of squeezing them. */
.tpl-bar > .tpl-file-status{flex-basis:100%;margin-top:0}

/* ---- compact list view --------------------------------------------------
   Today's dense rows, kept — plus a 34px thumb. Someone who knows the filename
   they want should not have to scroll a wall of pictures to find it. The view
   choice persists in localStorage (same pattern as the guide's other prefs). */
.tpl-file-thumb{position:relative;width:34px;height:34px;flex-shrink:0;border:1px solid var(--border);
  border-radius:var(--r-sm);background:var(--bg2);display:grid;place-items:center;overflow:hidden}
/* inset:0 again — the third and last of these. Even at a fixed 34px the
   percentage clamp did not hold a portrait asset (it measured 40px tall), so all
   three preview wells size their image the same provable way rather than each
   trusting a percentage against a different kind of parent. */
.tpl-file-thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;padding:3px}
.tpl-file-thumb .icon{width:14px;height:14px;color:var(--dim);stroke-width:1.5}
/* The row thumb is a <button> whenever there is something to preview, so it takes
   the same padding reset every other bare-button surface here does. Anything with
   nothing behind the click stays a <div> and never lands on these rules. */
button.tpl-file-thumb{padding:0;cursor:pointer;transition:border-color var(--d1) var(--ease-out)}
button.tpl-file-thumb:hover{border-color:var(--text)}
button.tpl-file-thumb[data-nopreview]{cursor:default}
button.tpl-file-thumb[data-nopreview]:hover{border-color:var(--border)}
.tpl-file--thumbed{gap:var(--sp-12)}
.tpl-file--thumbed .tpl-file-main{flex:1}
/* A folder in list view is a row like everything else — the view switch is a
   switch on the folder, not just on the files in it. The name and thumb sit in
   the same two columns a file row uses, so the list reads down one edge:
   .tpl-pick--gap holds the checkbox column open (a folder is not pickable — it
   travels whole, by the zip button on the right), and the open button carries
   the row's own 12px gap because it, not the <li>, is the flex parent of the
   thumb and the name. */
.tpl-pick--gap{width:20px;flex-shrink:0}
.tpl-folder-row-open{flex:1;display:flex;align-items:center;gap:var(--sp-12);min-width:0;
  padding:0;border:0;background:none;text-align:left;cursor:pointer}
.tpl-folder-row .tpl-file-thumb .icon{width:15px;height:15px;stroke-width:1.6}

/* ---- quick look ---------------------------------------------------------
   Click the preview, get the file — a 1024px render for artwork, the document
   itself for a deck or a PDF. This is what actually answers "see what the item
   is": the tile tells you which file, the quick look confirms it before a 40 MB
   download. Built on the same overlay material as .nc-dialog. */
.tpl-ql-overlay{position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,.62);display:none;
  place-items:center;padding:var(--sp-24);opacity:0;transition:opacity var(--d2) var(--ease-out)}
.tpl-ql-overlay.nc-open{display:grid;opacity:1}
.tpl-ql{width:min(760px,100%);background:var(--bg1);border:1px solid var(--border);border-radius:var(--r-xl);
  overflow:hidden;box-shadow:var(--elev-4),var(--hairline);transform:scale(.97);
  transition:transform var(--spring-dur-2) var(--spring-2)}
.tpl-ql-overlay.nc-open .tpl-ql{transform:scale(1)}
.tpl-ql-stage{position:relative;display:grid;place-items:center;min-height:min(52vh,420px);
  border-bottom:1px solid var(--border)}
/* The background is held off the rule above on purpose. .tpl-thumb--dark is a
   single-class selector too, so a background there would tie on specificity and
   win on source order — leaving a white-ink lockup as a white box at 420px,
   which is the one case that backing exists for. (.tpl-thumb--alpha sets only a
   background-image, so it composites over this and needs no exception.) */
.tpl-ql-stage:not(.tpl-thumb--dark){background:var(--bg2)}
.tpl-ql-stage img{max-width:100%;max-height:min(52vh,420px);object-fit:contain;padding:var(--sp-32);display:block}
/* A document is read, not looked at, so it gets the room a picture doesn't: a
   wider panel and a stage tall enough that a slide is legible and a page of A4
   shows most of itself before you scroll. The frame fills the stage rather than
   being padded into it — the PDF viewer draws its own margins. */
.tpl-ql--doc{width:min(1040px,100%)}
.tpl-ql--doc .tpl-ql-stage{min-height:min(76vh,720px);background:var(--bg1)}
.tpl-ql-frame{width:100%;height:min(76vh,720px);border:0;display:block;background:var(--bg2)}
/* notice()'s panel, reused on the stage: the burning flame while Box renders a
   deck for the first time, and whatever there is to say if it can't. */
.tpl-ql-note{border:0;background:none;padding:var(--sp-32)}
.tpl-ql-foot{display:flex;align-items:center;gap:var(--sp-16);padding:var(--sp-16) var(--sp-20)}
.tpl-ql-name{font-family:var(--fd);font-weight:600;font-size:var(--fs-h6);color:var(--text);line-height:1.2}
.tpl-ql-meta{font-family:var(--fb);font-size:var(--fs-caption);color:var(--dim);margin-top:var(--sp-2);
  font-variant-numeric:tabular-nums}
.tpl-ql-actions{margin-left:auto;display:flex;gap:var(--sp-8);flex-shrink:0}
.tpl-ql-close{position:absolute;top:var(--sp-10);right:var(--sp-10);width:32px;height:32px;display:grid;place-items:center;
  border:1px solid var(--border);border-radius:50%;background:var(--bg-glass);color:var(--text);cursor:pointer}
.tpl-ql-close .icon{width:15px;height:15px}

@media (max-width:600px){
  /* One step down the same scale (--sp-40 x 4 = 160px), not a fourth number. */
  .tpl-tiles{--grid-min:calc(var(--sp-40) * 4);gap:var(--sp-12)}
  .tpl-ql-foot{flex-wrap:wrap}
  .tpl-ql-actions{margin-left:0;width:100%}
}
@media (prefers-reduced-motion:reduce){
  .tpl-folder,.tpl-tile,.tpl-icon-btn,.tpl-ql,.tpl-ql-overlay{transition:none}
  .tpl-folder:hover,.tpl-folder-open:active,.tpl-tile:hover,.tpl-tile:focus-within{transform:none}
}

/* ── FILTER ── */
.fbar{display:flex;gap:6px;margin-bottom:20px;flex-wrap:wrap;align-items:center}
.fl{font-size:14px;color:var(--muted);margin-right:2px}
.fc{display:inline-flex;align-items:center;justify-content:center;font-size:12px;line-height:1.6;font-weight:600;padding:4px 11px;border-radius:var(--r-pill);border:1px solid var(--b2);background:var(--bg1);color:var(--muted);cursor:pointer;transition:background var(--d1) var(--ease-out),color var(--d1) var(--ease-out),border-color var(--d1) var(--ease-out),box-shadow var(--d1) var(--ease-out),transform var(--d1) var(--ease-out);font-family:var(--fb);text-transform:uppercase;letter-spacing:var(--tr-sm)}
.fc:hover{color:var(--text);border-color:var(--text);background:var(--bg2)}.fc.on{background:var(--s);color:var(--on-accent);border-color:var(--s)}

/* ── TOOL CARDS ── */
.tc{background:var(--bg1);border:1px solid var(--border);border-radius:var(--rl);overflow:hidden;transition:border-color var(--d1),box-shadow var(--d1),transform var(--d1)}
/* Superseded for box-shadow by the SECTION CARDS block further down this file
   (same specificity, later wins) — kept in sync so the two never disagree. */
.tc:hover{border-color:var(--b2);box-shadow:var(--elev-card-hover);transform:translateY(-1px)}
.tc.open{border-color:var(--b2);box-shadow:var(--elev-3),var(--hairline)}
.tc-hdr{padding:14px 16px 10px;display:flex;align-items:flex-start;justify-content:space-between;gap:10px;cursor:pointer}
.tc-name{font-family:var(--fd);font-size:15px;font-weight:600;margin-bottom:2px;color:var(--text)}
.tc-tag{font-size:14px;color:var(--muted);line-height:1.4}
.tc-badges{display:flex;gap:4px;flex-wrap:wrap;margin-top:6px}
.badge{display:inline-flex;align-items:center;justify-content:center;font-size:11px;line-height:1.6;font-weight:600;padding:2px 8px;border-radius:var(--r-pill);letter-spacing:var(--tr-sm);text-transform:uppercase;font-family:var(--fb);color:var(--muted);background:var(--bg2);border:1px solid var(--border)}
.xi{width:24px;height:24px;border-radius:50%;border:1px solid var(--border);background:var(--bg2);display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:14px;flex-shrink:0;transition:transform var(--d2),background var(--d1);user-select:none;line-height:1}
.tc.open .xi{transform:rotate(45deg);background:var(--bg3)}
.tc-body{max-height:0;overflow:hidden;background:var(--bg);transition:max-height var(--d2) var(--ease-out)}
.tc.open>.tc-hdr{border-bottom:1px solid var(--border)}

/* ── CARD INTERNALS ── */
.steps{padding:16px}
.step{display:flex;gap:12px;margin-bottom:16px;position:relative}
.step:last-child{margin-bottom:0}
.step::before{content:'';position:absolute;left:13px;top:27px;bottom:-16px;width:1px;background:var(--b2)}
.step:last-child::before{display:none}
.sn{width:26px;height:26px;border-radius:50%;border:1.5px solid var(--b2);display:flex;align-items:center;justify-content:center;font-family:var(--fb);font-size:12px;color:var(--muted);flex-shrink:0;background:var(--bg1);position:relative;z-index:1}
.st{font-size:14px;font-weight:600;color:var(--text);margin-bottom:3px;padding-top:4px}
.sb{font-size:14px;color:var(--muted);line-height:1.65}
.ex-box{margin:0 16px 13px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);overflow:hidden}
.ex-lbl{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--dim);padding:7px 11px;border-bottom:1px solid var(--border);background:var(--bg3)}
.ex-body{padding:11px}
.ex-body p{font-size:14px;color:var(--muted);margin-bottom:7px;line-height:1.65}
.ex-body p:last-child{margin-bottom:0}
.prompt{font-family:var(--fcode);font-size:12px;color:var(--text);background:var(--bg2);border:1px solid var(--border);border-left:2px solid var(--b3);padding:7px 10px;border-radius:var(--r-xs);margin:5px 0;line-height:1.55;word-break:break-word}
/* Notes read as neutral callouts distinguished by their label + icon; only the
   flag note keeps red (attention). Base covers every variant incl. day/family/size. */
.nt{margin:0 16px 13px;padding:10px 12px;border-radius:var(--r);font-size:14px;line-height:1.65;
  background:var(--bg2);border:1px solid var(--border);border-left:3px solid var(--b3);color:var(--muted)}
.nt strong{color:var(--text)}
.nt .icon{color:var(--muted)}
.nt-sacred{border-left-color:var(--text)}
.nt-sacred strong{color:var(--text)}
.nt-flag{background:var(--red2);border-color:var(--red);border-left-color:var(--red);color:var(--red)}
.nt-flag strong,.nt-flag .icon{color:var(--red)}

/* ── PROGRAM ASSET MAP ── */
.prog-card{background:var(--bg1);border:1px solid var(--border);border-radius:var(--rl);overflow:hidden;transition:box-shadow var(--d1)}
.prog-card:hover{box-shadow:var(--elev-card-hover)}
.prog-hdr{padding:13px 15px 10px;display:flex;justify-content:space-between;align-items:flex-start;gap:10px;cursor:pointer}
.prog-hdr:hover{background:var(--bg2)}
/* .archive-card is deliberately absent: it is a list row now, not a card, and a
   lift-on-hover made a 13-row list twitch under the pointer. Its own hover is the
   --tint wash on .arc-row-btn. Same reason it is out of the Phase 1 lift below. */
.vakya-card,.rm-card,.sk-card,.cr-card,.wf-card{transition:border-color var(--d1),box-shadow var(--d1),transform var(--d1)}
.vakya-card:hover,.rm-card:hover,.sk-card:hover,.cr-card:hover,.wf-card:hover{border-color:var(--b2);box-shadow:var(--elev-card-hover);transform:translateY(-1px)}
.prog-tier{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--s);margin-bottom:3px}
.prog-name{font-family:var(--fd);font-size:14px;font-weight:600;color:var(--text);margin-bottom:2px}
.prog-slot{font-size:14px;color:var(--muted)}
.prog-xi{width:22px;height:22px;border-radius:50%;border:1px solid var(--border);background:var(--bg2);display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:13px;flex-shrink:0;transition:transform var(--d2)}
.prog-card.open .prog-xi{transform:rotate(45deg)}
.prog-card.open .prog-hdr{border-bottom:1px solid var(--border)}
.prog-body{display:none;padding:13px 15px;background:var(--bg)}.prog-card.open .prog-body{display:block}
.prog-msg{font-size:14px;color:var(--muted);line-height:1.7;margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid var(--border)}
.prog-msg strong{color:var(--text)}
.deliverable-list{list-style:none}
.deliverable-list li{font-size:14px;color:var(--muted);padding:5px 0;border-bottom:1px solid var(--border);line-height:1.55;display:flex;gap:8px;align-items:flex-start}
.deliverable-list li:last-child{border-bottom:none}
/* Disciplines: monochrome chip + glyph + label (no hue), per spec §5.
   Flag keeps red as an attention/pending signal, matching .nt-flag. */
.del-type{font-family:var(--fb);font-size:11px;font-weight:600;padding:2px 7px;border-radius:var(--r-pill);flex-shrink:0;margin-top:2px;letter-spacing:var(--tr-sm);text-transform:uppercase;display:inline-flex;align-items:center;gap:4px;background:var(--bg2);color:var(--muted);border:1px solid var(--border);line-height:1.4}
.del-type::before{font-weight:400;font-size:1.05em;line-height:1}
.dt-video::before{content:"\25B6"}   /* ▶ */
.dt-led::before{content:"\25A3"}     /* ▣ */
.dt-live::before{content:"\25C9"}    /* ◉ */
.dt-audio::before{content:"\266A"}   /* ♪ */
.dt-graphic::before{content:"\25C6"} /* ◆ */
.dt-flag::before{content:"\2691"}    /* ⚑ */
.dt-flag{color:var(--red);border-color:var(--red);background:var(--red2)}

/* ── ARCHIVE ──
   A request tracker, not a card wall. It used to be 13 always-open cards in a
   3-column grid: ~90 bullet items and 52 status buttons on screen at once, ragged
   row heights because the grid sized each row to its tallest card, and no way to
   see what was outstanding without reading all ~5200px of it. Now: a rollup band,
   a filter row, and one collapsed row per request grouped under its priority.
   Behaviour is js/archiveTracker.js. */

/* On a desktop canvas the introduction and live rollup share the first band.
   Everything operational below spans the full width. At narrower viewports the
   original document order naturally becomes the single-column layout again.
   ⚠️ Route-scoped to body.view-archive because it sets `display`: a bare
   `#archive` selector (1,0,0) outranks the pre-route hiding rule above,
   `html:not(.nc-routed) .asset-sec` (0,2,1), so the section would paint on
   every page load before the router runs — the exact flash that rule exists
   to kill. showView() adds .view-archive in the same frame as .nc-routed. */
body.view-archive #archive{width:100%;min-width:0;display:grid;
  grid-template-columns:minmax(360px,720px) minmax(480px,1fr);
  column-gap:var(--sp-32);align-items:start}
/* The intro band spans BOTH columns: a display title at up to 68px inside a
   720px column would wrap to three lines beside a rollup card half its height.
   It is the section's opening statement, so it gets the full canvas and the
   two-column split starts underneath it. */
#archive>.nc-section-intro{grid-column:1/-1;grid-row:1}
#archive>.sec-d{grid-column:1;grid-row:2;margin-bottom:var(--sp-32)}
#archive>.arc-rollup{grid-column:2;grid-row:2;align-self:stretch;min-width:0;
  margin-bottom:var(--sp-32)}
#archive>.arc-toolbar,#archive>.arc-add-form,#archive>.arc-groups,#archive>.arc-empty{
  grid-column:1/-1}

/* rollup band — progress by status, and the counts double as status filters */
.arc-rollup{background:var(--bg1);border:1px solid var(--border);border-radius:var(--rl);
  box-shadow:var(--elev-tile);padding:var(--sp-20) var(--sp-24);margin-bottom:var(--sp-14);
  display:flex;flex-direction:column;justify-content:space-between}
.arc-rollup-top{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-12);flex-wrap:wrap}
.arc-rollup-lede{font-family:var(--fd);font-size:var(--fs-h6);color:var(--text)}
.arc-rollup-lede strong,.arc-rollup-lede span{font-variant-numeric:tabular-nums}
.arc-rollup-meta{font-family:var(--fb);font-size:var(--fs-caption);color:var(--muted)}
.arc-bar{display:flex;height:6px;border-radius:var(--r-pill);overflow:hidden;
  background:var(--bg3);margin:var(--sp-12) 0}
.arc-bar-seg{transition:width var(--d2) var(--ease-out)}
/* Fill, not hue, carries the meaning (same ladder as .nc-badge): done = solid ink,
   in-flight = accent, problem = red, untouched = the bare track. */
.arc-bar-seg[data-status="received"]{background:var(--text)}
.arc-bar-seg[data-status="submitted"]{background:var(--s)}
.arc-bar-seg[data-status="gap"]{background:var(--red)}
.arc-bar-seg[data-status="not-submitted"]{background:var(--b2)}
.arc-stats{display:flex;justify-content:space-between;gap:var(--sp-4);flex-wrap:wrap}
.arc-stat{display:inline-flex;align-items:baseline;gap:var(--sp-6);cursor:pointer;
  font-family:var(--fb);font-size:var(--fs-caption);color:var(--muted);
  padding:var(--sp-4) var(--sp-10);border-radius:var(--r-pill);
  border:1px solid transparent;background:transparent;
  transition:background var(--d1) var(--ease-out),color var(--d1) var(--ease-out),border-color var(--d1) var(--ease-out)}
.arc-stat-n{font-family:var(--fb);font-size:var(--fs-body-lg);font-weight:600;
  color:var(--text);font-variant-numeric:tabular-nums}
.arc-stat-l::before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;
  margin-right:var(--sp-6);vertical-align:1px;background:var(--b2)}
.arc-stat[data-status="received"] .arc-stat-l::before{background:var(--text)}
.arc-stat[data-status="submitted"] .arc-stat-l::before{background:var(--s)}
.arc-stat[data-status="gap"] .arc-stat-l::before{background:var(--red)}
.arc-stat:hover{background:var(--tint);color:var(--text)}
.arc-stat[aria-pressed="true"]{background:var(--tint2);border-color:var(--b2);color:var(--text)}

/* filter row */
.arc-toolbar{display:flex;align-items:center;gap:var(--sp-10);flex-wrap:wrap;margin-bottom:var(--sp-16)}
.arc-search{position:relative;display:flex;align-items:center;flex:1 1 240px;min-width:190px}
.arc-search .icon{position:absolute;left:12px;width:14px;height:14px;color:var(--dim);pointer-events:none}
.arc-search input{width:100%;font-family:var(--fb);font-size:var(--fs-body-sm);color:var(--text);
  background:var(--bg1);border:1px solid var(--b2);border-radius:var(--r-pill);
  padding:var(--sp-8) var(--sp-14) var(--sp-8) 33px;outline:none;
  transition:border-color var(--d1),box-shadow var(--d2) var(--ease-out)}
.arc-search input::placeholder{color:var(--dim)}
.arc-search input:focus{border-color:var(--s);box-shadow:var(--ring)}
.arc-search input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}
.arc-pfilters{display:flex;gap:var(--sp-6);flex-wrap:wrap}
.arc-pfilter-n{font-variant-numeric:tabular-nums;opacity:.6}
.arc-toolbar-end{display:flex;gap:var(--sp-6);margin-left:auto}

/* priority groups */
.arc-groups{min-width:0;display:flex;flex-direction:column;gap:var(--sp-24)}
.arc-group[hidden]{display:none}
.arc-group-head{display:flex;align-items:baseline;gap:var(--sp-10);
  padding-bottom:var(--sp-8);margin-bottom:var(--sp-10);border-bottom:1px solid var(--border)}
.arc-group-name{font-family:var(--fb);font-size:var(--fs-label);letter-spacing:var(--tr-md);
  text-transform:uppercase;font-weight:600;color:var(--text);margin:0}
.arc-group[data-priority="critical"] .arc-group-name{color:var(--red)}
.arc-group-note{font-family:var(--fb);font-size:var(--fs-caption);color:var(--dim)}
.arc-group-count{margin-left:auto;flex-shrink:0;font-family:var(--fb);font-size:var(--fs-caption);
  color:var(--muted);font-variant-numeric:tabular-nums}
.arc-rows{min-width:0;display:flex;flex-direction:column;gap:var(--sp-6)}

/* one request per row */
.archive-card{width:100%;min-width:0;background:var(--bg1);border:1px solid var(--border);
  border-radius:var(--r);overflow:hidden}
.archive-card[hidden]{display:none}
.arc-row{min-width:0;display:flex;align-items:center;gap:var(--sp-10);padding-right:var(--sp-12);
  transition:background var(--d1) var(--ease-out)}
.arc-row-btn{flex:1;min-width:0;display:flex;align-items:center;gap:var(--sp-10);
  padding:var(--sp-10) var(--sp-12);font:inherit;color:inherit;text-align:left;
  background:transparent;border:0;cursor:pointer}
/* The row owns the hover surface so the status control is part of the same
   rounded highlight. Painting only .arc-row-btn produced a hard rectangular
   stop immediately before the select. */
.arc-row:hover,.arc-row:focus-within{background:var(--tint)}
.arc-row-btn:hover{background:transparent}
.arc-chev{display:flex;flex-shrink:0;color:var(--dim);
  transition:transform var(--d2) var(--ease-out),color var(--d1)}
.arc-chev .icon{width:14px;height:14px}
.arc-open .arc-chev{transform:rotate(90deg);color:var(--text)}
.arc-row-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.arc-row-main .archive-name{font-family:var(--fd);font-size:var(--fs-lead);font-weight:600;
  color:var(--text);margin:0;line-height:1.35}
.arc-row-main .archive-cat{font-family:var(--fb);font-size:var(--fs-caption);letter-spacing:var(--tr-md);
  text-transform:uppercase;color:var(--muted);margin:0;line-height:1.4}
.arc-warn{color:var(--red);width:15px;height:15px}
.arc-row-items{flex-shrink:0;font-family:var(--fb);font-size:var(--fs-body-sm);
  color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap}

/* status pill. Same construction as pm.css's .cell-status-select and for the same
   reason: `select.` beats the (0,0,1) base in css/primitives.css so the chevron
   can be dropped — a fixed data-URI stroke has no colour that reads on both an
   orange fill and a solid-ink one — and the affordance becomes a hover ring. */
/* ⚠️ min-width is the fix for a clipped label, not a layout preference. A
   <select> left to size itself takes EXACTLY its widest option's text plus its
   own padding and border — measured here: "NOT SUBMITTED" needs 123.32px and the
   browser handed the pill 123.33px. Zero slack, so whether the final "D" paints
   or gets shaved is left to sub-pixel rounding, and it loses on a headed Chrome
   at a non-100% zoom. Padding cannot buy the room back: it is inside the
   intrinsic width, so widening it moves both edges and the text still ends flush.
   An explicit floor is the only lever. 12.5em (137.5px at --fs-micro) clears the
   widest label by ~12px, covers the fallback stack too (SF Pro renders the same
   string 2px wider than Inter), and tracks --fs-micro rather than pinning a
   measurement in px — that token has moved once already (10 -> 11px, see #78).
   It also stops the pill resizing under the pointer on every status change. */
select.arc-status-select{flex-shrink:0;height:24px;min-width:12.5em;max-width:none;
  padding:0 var(--sp-10);
  border-style:solid;border-width:1px;border-radius:var(--r-pill);
  font-family:var(--fb);font-size:var(--fs-micro);line-height:1;font-weight:600;
  text-transform:uppercase;letter-spacing:var(--tr-sm);
  background-image:none;text-align:center;text-align-last:center}
.arc-status-select[data-status="not-submitted"]{background-color:var(--bg2);border-color:var(--border);color:var(--muted)}
.arc-status-select[data-status="submitted"]{background-color:var(--s);border-color:var(--s);color:var(--on-accent)}
.arc-status-select[data-status="received"]{background-color:var(--text);border-color:var(--text);color:var(--bg)}
.arc-status-select[data-status="gap"]{background-color:var(--red2);border-color:var(--red);color:var(--red)}
.arc-status-select:hover{box-shadow:0 0 0 3px var(--tint)}

/* the items, revealed */
.arc-panel{min-width:0;padding:var(--sp-16) var(--sp-24) var(--sp-20) 48px;
  border-top:1px solid var(--border)}
.archive-items{min-width:0;list-style:none;margin:0;padding:0}
.archive-items li{min-width:0;display:grid;grid-template-columns:16px minmax(0,1fr);
  column-gap:var(--sp-8);font-size:var(--fs-lead);color:var(--text);
  padding:var(--sp-10) 0;border-bottom:1px solid var(--border);line-height:1.65;
  overflow-wrap:anywhere;break-inside:avoid}
.archive-items li:last-child{border-bottom:none}
.archive-items li::before{content:"→";color:var(--dim)}
.arc-empty{font-family:var(--fb);font-size:var(--fs-body-sm);color:var(--muted);
  text-align:center;padding:var(--sp-32) 0;margin:0}
.arc-empty[hidden]{display:none}

/* Expanded requests split into readable columns once there is canvas to spare.
   ⚠️ column-WIDTH, not column-count:2. A fixed count widens its columns as the
   canvas grows, so it fails the very thing it is here for — measured in Chrome
   with the rail collapsed, `column-count:2` gave 93 characters per line at
   1920, 129 at 2560 and 178 at 3440. Asking for a 24rem measure instead lets
   the browser add columns rather than stretch them: every viewport/rail
   combination from 1280 to 3440 now lands between 44 and 66 characters, and
   the count is identical to the old rule at 1600 and below. It also sidesteps
   the rail entirely — @media measures the viewport, but the panel's real box
   is viewport minus a 268px (or 78px collapsed) rail, and column-width
   resolves against that real box. */
@media(min-width:1280px){
  .archive-items{column-width:24rem;column-gap:var(--sp-40);column-rule:1px solid var(--border)}
}
/* 1279, not 1280 — the repo pairs breakpoints on adjacent integers (960/959,
   921/920) so exactly one side of a crossover ever matches. */
@media(max-width:1279px){
  body.view-archive #archive{display:block}
  #archive>.arc-rollup{margin-bottom:var(--sp-20)}
}

/* ── VAKYA CARDS ── */
.vakya-card{background:var(--bg1);border:1px solid var(--border);border-radius:var(--rl);padding:16px}
.vakya-q{font-size:14px;color:var(--muted);margin-bottom:8px;line-height:1.6;font-style:italic}
.vakya-a{font-family:var(--fd);font-size:19px;color:var(--text);line-height:1.5;margin-bottom:7px}
.vakya-en{font-size:14px;color:var(--muted);line-height:1.6;padding-top:7px;border-top:1px solid var(--border)}
.vakya-use{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-sm);text-transform:uppercase;color:var(--s-text);margin-top:9px;display:block}

/* ── VAKYAS ROTATOR (Overview section) ── */
/* The stage fills the screen under the top bar by growing into .content-wrap's
   leftover column space, so there is no bar height to guess at. `1 0 auto`, not
   `1`: flex-shrink stays 0 and the basis stays auto so a short window (or a
   quote that wraps to five lines) grows the page instead of shrinking the stage
   — overflow:hidden below would otherwise let it clip its own quote, since a
   scroll container's automatic minimum size is 0. */
#tiers{flex:1 0 auto;display:flex;flex-direction:column}
/* touch-action: horizontal pans belong to the swipe handler in script.js, vertical
   ones stay page scrolls. Naming pinch-zoom keeps a bare `pan-y` from taking
   zooming away on an element that, per the note above, is the whole screen. */
.vr-stage{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;flex:1 0 auto;padding:64px 24px 48px;text-align:center;overflow:hidden;touch-action:pan-y pinch-zoom}
/* The wash is three 60vmax circles inside that overflow:hidden, so the stage's
   bottom border sliced them off mid-blob and drew a hard line where the warm
   tint met the white page below. Fade it out before the edge instead, the way
   .hero does. */
.vr-stage > .nc-ambient{-webkit-mask-image:linear-gradient(to bottom,#000 55%,transparent);mask-image:linear-gradient(to bottom,#000 55%,transparent)}
.vr-stage .nc-ambient span:nth-child(1){background:radial-gradient(circle,rgba(190,58,52,.12),transparent 62%)}
.vr-stage .nc-ambient span:nth-child(2){background:radial-gradient(circle,rgba(190,58,52,.08),transparent 62%)}
.vr-stage .nc-ambient span:nth-child(3){background:radial-gradient(circle,rgba(190,58,52,.05),transparent 62%)}
.vr-label{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-lg);text-transform:uppercase;color:var(--muted);margin-bottom:48px;display:flex;align-items:center;gap:14px}
.vr-label::before,.vr-label::after{content:'';width:24px;height:1px;background:var(--s);opacity:.6}
.vr-slides{display:grid;width:100%;max-width:760px;margin:0 auto;overflow:hidden}
.vr-slide{grid-column:1;grid-row:1;opacity:0;transform:translateX(0);pointer-events:none;display:flex;flex-direction:column;align-items:center;gap:18px}
.vr-slide.is-in{opacity:1;transform:translateX(0);pointer-events:auto;transition:opacity var(--d3) var(--ease-in-out),transform var(--d3) var(--ease-in-out)}
.vr-slide.exit-left{opacity:0;transform:translateX(-52px);pointer-events:none;transition:opacity var(--d3) var(--ease-in-out),transform var(--d3) var(--ease-in-out)}
.vr-slide.exit-right{opacity:0;transform:translateX(52px);pointer-events:none;transition:opacity var(--d3) var(--ease-in-out),transform var(--d3) var(--ease-in-out)}
.vr-btn{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:44px;border-radius:50%;border:1px solid var(--b2);background:var(--bg1);color:var(--muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background var(--d1),color var(--d1),border-color var(--d1);z-index:1}
.vr-btn:hover{background:var(--bg2);color:var(--text);border-color:var(--b3)}
.vr-btn .icon{width:18px;height:18px}
.vr-btn-prev{left:20px}
.vr-btn-next{right:20px}
/* On narrow screens the edge-pinned arrows sat ON TOP of the quote: at 390px the
   buttons occupied 8–44px and 346–382px while the text column ran 20–370px. Drop them
   to the bottom corners so they flank the dots instead of overlaying the vakya.
   Kept at 44px — Vercel's guideline is a 44px minimum touch target on mobile. */
@media (max-width:640px){
  .vr-btn{top:auto;bottom:14px;transform:none;width:44px;height:44px}
  .vr-btn-prev{left:10px}
  .vr-btn-next{right:10px}
  .vr-stage{padding-bottom:72px}   /* room for the arrow row */
}
.vr-q{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-lg);text-transform:uppercase;color:var(--muted);line-height:1.5}
.vr-badge{display:inline-block;font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--s-text);background:var(--s2);border-radius:var(--r-pill);padding:2px 8px;margin-right:6px;vertical-align:middle}
/* Both lines of the vakya are balanced, and both for the same measured reason:
   the ragged last line. The quote broke 28/27/17ch at 1440 and 31/35/6ch at
   390 (a line holding nothing but "pāye."); the gloss broke 57/51ch at 1440 and
   45/48/15ch at 390 ("purush maryada." alone). balance gives 21/27/25, 54/54
   and 39/35/34 — same line count, same height, so the stage does not resize.
   pretty was measured here first and changed nothing at any of the three
   widths: it only guards the last line and Chrome declines the move when the
   text is this short, which is exactly the case balance is for. */
.vr-a{font-family:var(--fd);font-size:clamp(26px,4.5vw,54px);font-weight:600;color:var(--text);line-height:1.22;letter-spacing:-.01em;max-width:680px;text-wrap:balance}
.vr-en{font-size:clamp(15px,1.6vw,18px);color:var(--muted);line-height:1.75;max-width:520px;text-wrap:balance}
.vr-footer{margin-top:56px;display:flex;flex-direction:column;align-items:center;gap:16px;position:relative;z-index:1}
.vr-dots{display:flex;gap:8px;align-items:center}
.vr-dot{width:6px;height:6px;border-radius:50%;background:var(--b2);border:none;cursor:pointer;padding:0;transition:background var(--d2),width var(--d2),border-radius var(--d2)}
.vr-dot.active{background:var(--s);width:22px;border-radius:var(--r-pill)}
.vr-dot:hover:not(.active){background:var(--dim)}
.vr-progress{width:100px;height:2px;background:var(--border);border-radius:1px;overflow:hidden}
.vr-progress-fill{height:100%;background:var(--s);width:0%;opacity:.6}
@media (max-width:640px){
  .vr-stage{padding:48px 20px 36px}
  /* The eyebrow lockup stacks below the phone breakpoint. "Swamishri's Vakyas ·
     Akshardham Foundation" sets 381px at 11px/.18em, and the row also spends 76px on
     the two 24px rules and their gaps; the stage leaves 350px at 390px wide, so
     the text takes two lines. In a row that left align-items:center holding the
     two rules at the midpoint BETWEEN those lines, touching neither. Stacking
     puts one rule above the text and one below, which is the same device read
     vertically. nowrap is not the alternative — the single line overflows
     390px. */
  .vr-label{flex-direction:column;gap:10px}
  .vr-a{font-size:clamp(22px,6vw,40px)}
  .vr-footer{margin-top:40px}
}

/* ── ROADMAP ── */
.rm-card{background:var(--bg1);border:1px solid var(--border);border-radius:var(--rl);padding:18px;position:relative;overflow:hidden}
.rm-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:var(--b2)}
/* roadmap phases read neutral; the auto-detected CURRENT phase gets the orange border below */
.rm-ph{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--muted);margin-bottom:4px}
.rm-ti{font-family:var(--fd);font-size:16px;font-weight:600;margin-bottom:2px;color:var(--text)}
.rm-dt{font-family:var(--fb);font-size:12px;color:var(--muted);margin-bottom:12px}
.rm-items{list-style:none}
.rm-items li{font-size:14px;color:var(--muted);padding:4px 0;border-bottom:1px solid var(--border);line-height:1.55}
.rm-items li:last-child{border-bottom:none}
.rm-items li::before{content:"› ";color:var(--dim)}

/* ── SKILLS ── */
.sk-card{background:var(--bg1);border:1px solid var(--border);border-radius:var(--rl);padding:18px}
.sk-pri{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-md);text-transform:uppercase;margin-bottom:6px;color:var(--muted)}
.sk-name{font-family:var(--fd);font-size:16px;font-weight:600;margin-bottom:2px;color:var(--text)}
.sk-type{font-size:14px;color:var(--muted);margin-bottom:8px;line-height:1.5}
.sk-time{font-family:var(--fb);font-size:12px;color:var(--muted);background:var(--bg2);border:1px solid var(--b2);padding:3px 8px;border-radius:var(--r-xs);display:inline-block;margin-bottom:10px}
.sk-res h4{font-size:12px;font-weight:600;color:var(--text);margin-bottom:5px;letter-spacing:var(--tr-sm);text-transform:uppercase;font-family:var(--fb)}
.sk-res ul{list-style:none}
.sk-res li{font-size:14px;color:var(--muted);padding:3px 0;border-bottom:1px solid var(--border);line-height:1.55}
.sk-res li:last-child{border-bottom:none}
.sk-res li::before{content:"↗ ";color:var(--dim);font-size:12px}
.sk-unlocks{margin-top:10px;padding:8px 10px;background:var(--bg2);border:1px solid var(--border);border-left:3px solid var(--b3);border-radius:var(--r-xs);font-size:14px;color:var(--muted);line-height:1.5}
.sk-unlocks strong{color:var(--text)}

/* ── CRUNCH ── */
.cr-card{background:var(--bg1);border:1px solid var(--border);border-radius:var(--rl);padding:18px}
.cr-ico{font-size:20px;margin-bottom:9px}
.cr-h{font-family:var(--fd);font-size:15px;font-weight:600;margin-bottom:5px;color:var(--text)}
.cr-b{font-size:14px;color:var(--muted);line-height:1.65}

/* ── AT A GLANCE (section digest — first-read overview above the tool cards) ── */
.glance{background:var(--bg1);border:1px solid var(--border);border-left:3px solid var(--s);border-radius:var(--rl);padding:15px 18px;margin-bottom:22px}
.glance-lbl{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--s-text);margin-bottom:7px}
.glance-intro{font-size:14px;color:var(--text);line-height:1.6;margin-bottom:13px;max-width:660px}
.glance-list{list-style:none}
.glance-item{display:flex;justify-content:space-between;gap:18px;align-items:baseline;padding:8px 0;border-top:1px solid var(--border)}
.glance-what{font-size:14px;color:var(--text)}
.glance-how{font-family:var(--fb);font-size:12px;color:var(--muted);text-align:right;flex-shrink:0}
.glance-more{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-sm);text-transform:uppercase;color:var(--dim);margin-top:11px}

@media(max-width:640px){
  .hero,.main{padding-left:18px;padding-right:18px}
  .search-wrap{padding-left:12px;padding-right:12px}
  .hero{padding-top:52px;padding-bottom:32px}
  .tg,.rm-grid,.sk-grid,.cr-grid,.prog-grid,.vakya-grid,.wf-grid{grid-template-columns:1fr}
  .glance-item{flex-direction:column;gap:2px}
  .glance-how{text-align:left}
  .ts{flex-direction:column}
  .tier{border-right:none;border-bottom:1px solid var(--border);min-width:0}
  .tier:last-child{border-bottom:none}
  /* A request row runs out of width for name + count + status pill well before
     the phone does, so the pill drops under the title rather than squeezing it. */
  .arc-row{flex-wrap:wrap;padding:0 var(--sp-12) var(--sp-10)}
  .arc-row-btn{flex-basis:100%;padding-left:0;padding-right:0;min-height:44px}
  .arc-row-items{margin-left:auto}
  .arc-toolbar-end{margin-left:0}
  .arc-panel{padding-left:var(--sp-16)}

  /* Sub-44px controls on this surface. css/primitives.css raises the shared
     floor; these three hard-code a height of their own (24-29px), so the shared
     min-height can't reach them. Same reasoning as the .vr-btn note above — a
     thumb, not a cursor. (File Drop's pair is in the block at the end of this
     file, where it comes after their base rules.) */
  select.arc-status-select{height:44px}
  .arc-stat{min-height:44px}
  .tpl-dl{min-height:44px}
}
@media(prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}

/* ── DARK MODE ── dark-palette token overrides now live in css/tokens.css */

/* ── MODE TOGGLE ── now a shared primitive in css/primitives.css. */

/* ── ARCHIVE ADD ENTRY ──
   The 4-button-per-card tracker that used to live here is gone: 13 cards x 4
   buttons put 52 equally-loud controls on the page to express one value each.
   One .arc-status-select per row replaced the lot (fills above). */
.arc-add-form{margin:0 0 var(--sp-16);padding:var(--sp-16);background:var(--bg2);
  border:1px solid var(--border);border-radius:var(--rl);
  display:flex;flex-direction:column;gap:var(--sp-10)}
.arc-add-form[hidden]{display:none}
.arc-add-row{display:flex;gap:var(--sp-10);flex-wrap:wrap;align-items:center}
.arc-add-input{flex:1;min-width:180px;background:var(--bg1);border:1px solid var(--b2);
  border-radius:var(--r);padding:var(--sp-8) var(--sp-10);font-family:var(--fb);
  font-size:var(--fs-body-sm);color:var(--text);outline:none;transition:border-color var(--d1)}
.arc-add-input::placeholder{color:var(--dim)}
.arc-add-input:focus{border-color:var(--s);box-shadow:var(--ring)}
.arc-add-textarea{resize:vertical;min-height:80px;flex:unset;width:100%;box-sizing:border-box}
.arc-add-select{min-width:240px}
.arc-add-actions{display:flex;gap:var(--sp-8);align-items:center;flex-shrink:0;margin-left:auto}
.arc-delete-btn{margin-top:var(--sp-12);font-family:var(--fb);font-size:var(--fs-caption);
  padding:var(--sp-4) var(--sp-10);border-radius:var(--r-pill);border:1px solid var(--border);
  background:transparent;color:var(--muted);cursor:pointer;
  transition:background var(--d1) var(--ease-out),color var(--d1) var(--ease-out),border-color var(--d1) var(--ease-out)}
.arc-delete-btn:hover{border-color:var(--red);color:var(--red);background:var(--red2)}
/* ── TOP BAR / SEARCH ──
   Now the page's only sticky bar: the old .app-topbar (wordmark + burger) was
   folded in when the rail took over navigation, so it sits at top:0. */
.search-wrap {
  position: sticky; top: 0; z-index: 90;
  background: var(--bg-chrome); border-bottom: 1px solid var(--border);
  padding: 10px 18px;
  display: flex; align-items: center; gap: 10px;
}
/* current-section label, standing in for the wordmark the rail now carries */
.topbar-crumb {
  font-size: var(--fs-body-sm); font-weight: 600; letter-spacing: -.01em;
  color: var(--text); white-space: nowrap; flex-shrink: 0;
}
@media(max-width:640px){ .topbar-crumb { display: none; } }
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .search-wrap { background: var(--bg-glass); backdrop-filter: blur(12px) saturate(1.3); -webkit-backdrop-filter: blur(12px) saturate(1.3); }
}
/* REMOVED 2026-08-19 along with the search bar itself: .search-input,
   .search-wrap-inner, .rail-search (+ its .rail-mini variants), .topbar-search,
   .search-ico, .search-kbd, .search-count, .search-clear, .search-no-results.
   The .search-wrap rules above are the TOP BAR, not search — the class name is
   historical, from when the bar existed to hold the field.

   #89 pill-styled this field (--r-pill on .search-input, 50% on .topbar-search)
   and its collapsed-rail disc morph. That styling went out with the field it
   styled; the pill pass itself is untouched everywhere else. #89 also noted the
   .rail-mini hover bug in these rules — moot now that the rules are gone. */

/* The crumb claims the free space, so the mode toggle stays pinned right. */
.topbar-crumb { margin-right: auto; }

/* ── CORE TOOL BADGE ── */
.badge-core {
  display:inline-flex;align-items:center;justify-content:center;
  font-size: 11px; line-height:1.6; font-weight: 700; padding: 2px 8px; border-radius: var(--r-pill);
  letter-spacing: .06em; text-transform: uppercase; font-family: var(--fb);
  background: var(--text); color: var(--bg); border: 1px solid var(--text);
}

/* ── SKILL ASSIGN TO ── */
.sk-assign {
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border);
  display: flex; align-items: center; gap: 8px;
}
.sk-assign-lbl {
  font-family: var(--fb); font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--dim); flex-shrink: 0;
}
.sk-assign-input {
  flex: 1; background: var(--bg2); border: 1px solid var(--b2);
  border-radius: var(--r-xs); padding: 4px 9px; font-family: var(--fb);
  font-size: 14px; color: var(--text); outline: none;
  transition: border-color var(--d1);
}
.sk-assign-input::placeholder { color: var(--dim); }
.sk-assign-input:focus { border-color: var(--s); }

/* ── TEAM WORKFLOW ── */
.wf-card { background: var(--bg1); border: 1px solid var(--border); border-radius: var(--rl); overflow: hidden; }
.wf-hdr { padding: 14px 16px 10px; border-bottom: 1px solid var(--border); background: var(--bg2); }
.wf-cat { font-family: var(--fb); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-bottom: 4px; }
.wf-title { font-family: var(--fd); font-size: 15px; font-weight: 600; color: var(--text); }
.wf-body { padding: 14px 16px; }
.wf-body p { font-size: 14px; color: var(--muted); line-height: 1.7; margin-bottom: 10px; }
.wf-body p:last-child { margin-bottom: 0; }
.wf-code { font-family: var(--fcode); font-size: 12px; color: var(--text); background: var(--bg2); border: 1px solid var(--border); border-left: 2px solid var(--b3); padding: 6px 10px; border-radius: var(--r-xs); margin: 6px 0; line-height: 1.7; word-break: break-all; }
/* --fcode, not --fb: the ├─ / └─ prefixes below are box-drawing characters that a UI
   sans is not guaranteed to carry, and this block sits next to .wf-code anyway. */
.wf-tree { list-style: none; font-family: var(--fcode); font-size: 12px; color: var(--muted); line-height: 1.9; }
.wf-tree li { padding-left: 0; }
.wf-tree .t1 { color: var(--text); font-weight: 600; }
.wf-tree .t2 { padding-left: 14px; }
.wf-tree .t2::before { content: "├─ "; color: var(--dim); }
.wf-tree .t2.last::before { content: "└─ "; color: var(--dim); }
.wf-tree .t3 { padding-left: 28px; }
.wf-tree .t3::before { content: "├─ "; color: var(--dim); }
.wf-tree .t3.last::before { content: "└─ "; color: var(--dim); }
.wf-rule { display: flex; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--border); align-items: flex-start; }
.wf-rule:last-child { border-bottom: none; }
.wf-rule-num { font-family: var(--fb); font-size: 14px; color: var(--text); flex-shrink: 0; padding-top: 2px; font-weight: 700; font-variant-numeric: tabular-nums; }
.wf-rule-text { font-size: 14px; color: var(--muted); line-height: 1.6; }
.wf-rule-text strong { color: var(--text); }


.prog-count {
  font-family: var(--fb); font-size: 14px; padding: 2px 7px;
  border-radius: var(--r); background: var(--bg2); border: 1px solid var(--b2);
  color: var(--muted); white-space: nowrap; flex-shrink: 0; margin-top: 2px;
font-variant-numeric:tabular-nums}

/* ── ASSET MAP: mandal groups, filter bar, type chips ── */
#assetmap .prog-grid{grid-template-columns:1fr;gap:20px}
.mandal-grp{display:flex;flex-direction:column;gap:8px}
.mandal-grp-hdr{display:flex;align-items:baseline;gap:10px;padding:10px 0 8px;border-bottom:2px solid var(--b2);margin-bottom:4px}
.mg-code{display:inline-flex;align-items:center;justify-content:center;font-family:var(--fb);font-size:11px;line-height:1.6;font-weight:700;letter-spacing:var(--tr-md);color:var(--s-text);flex-shrink:0;padding:2px 8px;border-radius:var(--r-pill);background:var(--s2);border:1px solid var(--s3)}
.mg-name{font-family:var(--fd);font-size:16px;font-weight:600;color:var(--text)}
.mg-stat{font-family:var(--fb);font-size:11px;color:var(--dim);letter-spacing:var(--tr-xs);margin-left:auto;white-space:nowrap}
.mandal-grp.am-hidden,.prog-card.am-hidden{display:none}

.asset-filter{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:16px;align-items:center}
.af-btn{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-sm);text-transform:uppercase;padding:4px 11px;border-radius:var(--r-pill);border:1px solid var(--border);background:var(--bg2);color:var(--muted);cursor:pointer;line-height:1.5;display:inline-flex;align-items:center;gap:4px;transition:background var(--d1),color var(--d1),border-color var(--d1)}
.af-btn:hover{background:var(--bg3);color:var(--text);border-color:var(--b2)}
.af-btn.on{background:var(--text);color:var(--bg);border-color:var(--text)}
.af-btn--flag.on{background:var(--red);color:var(--on-red);border-color:var(--red)}
.af-btn .icon{width:11px;height:11px;flex-shrink:0}
.af-label{font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--dim);flex-shrink:0;padding:4px 2px;align-self:center}
.af-mandal{display:inline-flex;align-items:center;justify-content:center;font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-sm);text-transform:uppercase;padding:4px 11px;border-radius:var(--r-pill);border:1px solid var(--border);background:var(--bg2);color:var(--muted);cursor:pointer;line-height:1.5;transition:background var(--d1),color var(--d1),border-color var(--d1)}
.af-mandal:hover{background:var(--bg3);color:var(--text);border-color:var(--b2)}
.af-mandal.on{background:var(--s);color:var(--on-accent);border-color:var(--s)}
.asset-filter--type{margin-top:-10px}

.prog-types{display:flex;gap:4px;flex-wrap:wrap;margin-top:6px}
.pt{display:inline-flex;align-items:center;justify-content:center;font-family:var(--fb);font-size:11px;letter-spacing:var(--tr-sm);text-transform:uppercase;padding:1px 7px;border-radius:var(--r-pill);background:var(--bg2);color:var(--muted);border:1px solid var(--border);line-height:1.6;white-space:nowrap}
.pt-video{color:var(--s-text);border-color:var(--s3);background:var(--s2)}
.pt-led{color:var(--text);border-color:var(--b2);background:var(--tint)}
.pt-live{color:var(--muted);border-color:var(--border);background:var(--bg2)}
.pt-audio{color:var(--muted);border-color:var(--border);background:var(--bg2)}
.pt-graphic{color:var(--muted);border-color:var(--border);background:var(--bg2)}
.pt-flag{color:var(--red);border-color:var(--red);background:var(--red2)}

.cr-card.cr-critical { border-left: 3px solid var(--red); }
.cr-card.cr-high     { border-left: 3px solid var(--text); }
.cr-card.cr-ops      { border-left: 3px solid var(--b2); }
.cr-label {
  display:inline-flex;align-items:center;justify-content:center;font-family: var(--fb); font-size: 11px;line-height:1.6;
  padding: 1px 6px; border-radius: var(--r-xs); margin-bottom: 7px;
  letter-spacing: .05em; text-transform: uppercase; font-weight: 600;
}
.cr-label.critical { background: var(--red2); color: var(--red); border: 1px solid var(--red); }
.cr-label.high     { background: var(--text); color: var(--bg); }
.cr-label.ops      { background: var(--bg2);  color: var(--muted); border: 1px solid var(--border); }

.rm-card.phase-active {
  border: 1.5px solid var(--s);
}
.rm-phase-badge {
  display:inline-flex;align-items:center;justify-content:center;font-family: var(--fb); font-size: 14px;line-height:1.6;
  padding: 2px 7px; border-radius: var(--r-pill); background: var(--s);
  color: var(--on-accent); margin-left: 8px; letter-spacing: .04em;
}

/* ── TOP PRIORITIES ── */
.priorities-box {
  margin: 0 0 0 0;
  background: var(--bg1);
  border-bottom: 1px solid var(--border);
  padding: 20px 40px 20px 72px;
}
.priorities-title {
  font-family: var(--fb); font-size: 12px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--text); margin-bottom: 12px;
  display: flex; align-items: center; gap: 10px;
}
.priorities-title::before { content: ''; width: 16px; height: 1px; background: var(--s); }
.priorities-grid {
  display: flex; gap: 8px; flex-wrap: wrap;
}
.priority-item {
  display: flex; align-items: flex-start; gap: 8px;
  background: var(--bg2); border: 1px solid var(--b2);
  border-radius: var(--r); padding: 10px 13px;
  font-size: 14px; color: var(--muted); line-height: 1.5;
  flex: 1; min-width: 200px; max-width: 320px;
}
.priority-item.urgent { border-color: var(--red); background: var(--red2); }
.priority-item.urgent .p-num { color: var(--red); }
.priority-num {
  font-family: var(--fb); font-size: 14px; font-weight: 700;
  color: var(--s); flex-shrink: 0; margin-top: 1px;
  font-variant-numeric: tabular-nums;
}
.priority-item strong { color: var(--text); display: block; margin-bottom: 2px; }
@media(max-width:640px){
  .priorities-box { padding: 16px 18px 16px 60px; }
  .priority-item { min-width: 100%; }
}

/* ── COUNTDOWN ── the creative-lock module now lives in the rail (css/sitenav.css);
   the old top-bar countdown card was removed with the nav change. */

/* ── MILESTONE PROGRESS BAR ── removed: the sticky Start → Lock → Convention
   timeline under the search bar is gone, along with its markup and script. */

:focus-visible{outline:2px solid var(--s);outline-offset:2px}

/* ── SECTION PREV/NEXT ── removed: the Previous/Next bar that closed every
   section is gone, along with script.js's buildPrevNextBars(). */

/* ── AUTH ── */
#app-shell{display:none}
/* Both shells start hidden; a pre-paint inline script (see shell.html) sets
   html.auth-in / html.auth-out from the persisted Supabase token, so a
   returning user lands straight on the app instead of flashing the login
   screen while auth.js resolves the session asynchronously. */
#login-screen{min-height:100vh;display:none;align-items:center;justify-content:center;background:var(--bg);padding:var(--sp-32) var(--sp-20)}
html.auth-in #app-shell{display:block}
html.auth-out #login-screen{display:flex}
.login-form{width:100%;max-width:400px;padding:var(--sp-40) var(--sp-32);background:var(--bg1);
  border:1px solid var(--border);border-radius:var(--r-xl);display:flex;flex-direction:column;gap:var(--sp-12)}
.login-title{font-family:var(--fd);font-weight:600;font-size:var(--fs-h4);line-height:1.1;color:var(--text)}
/* Two lines in a 334px card, and they broke 51ch/24ch — a deck under a title is
   the shape balance exists for. Now 37/38. */
.login-sub{color:var(--muted);font-size:var(--fs-body-sm);margin-bottom:var(--sp-4);text-wrap:balance}
/* Field labels read as the design system's micro labels, not as body text — the
   input is the loud element, the label just names it. */
.login-form label{display:flex;flex-direction:column;gap:var(--sp-6);font-family:var(--fb);
  font-size:var(--fs-label);font-weight:500;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--muted)}
/* The two-step login (email, then code) toggles fields with [hidden], which the
   display:flex above would otherwise override. */
.login-form [hidden]{display:none}
/* Inputs and the submit button ride the shared primitives (.nc-field,
   .nc-btn--primary) — these only resize them to the login screen's comfort. */
.login-form .nc-field{padding:var(--sp-12) var(--sp-14);font-size:var(--fs-body-lg)}
.login-form .nc-field::placeholder{color:var(--dim)}
/* The visible half of the aria-invalid js/auth.js sets on whichever field the
   message belongs to, so the marking is not screen-reader-only. Border colour
   alone, matching #login-error rather than shouting louder than it. */
.login-form .nc-field[aria-invalid="true"]{border-color:var(--red)}
/* The code arrives letter-spaced in the email; typing it back into a tracked,
   tabular field makes a mistyped digit visible instead of buried in a run. */
#login-code-field .nc-field{font-size:var(--fs-h6);letter-spacing:.22em;font-variant-numeric:tabular-nums}
.login-form button{padding:var(--sp-12);font-size:var(--fs-body-lg);font-weight:600}
#login-restart{font-size:var(--fs-caption);color:var(--s-text);text-align:center}
/* The resend reads as a line of help text with one live word in it, not as a
   second button: in a 400px card with a filled "Log in" directly above, a
   second bordered button would read as an equal choice, and it is not one.
   Sized and coloured to match #login-restart, the other escape hatch on this
   step, so the two read as one tier. */
.login-resend-row{font-size:var(--fs-caption);color:var(--muted);text-align:center}
/* Underlined, like the #login-restart link under it: in this palette --s-text
   and --muted are close enough in light mode that colour alone does not read as
   actionable, and the better of the two escape hatches must not look like the
   less clickable one. No padding either, with the gap to the question mark
   carried by a margin, because the focus ring is drawn 2px outside the box and a
   padded box pushes that ring into the preceding glyph rather than away. */
#login-resend{padding:0;margin-left:var(--sp-4);border:0;font-size:var(--fs-caption);font-weight:500;
  color:var(--s-text);text-decoration:underline;text-underline-offset:2px;font-variant-numeric:tabular-nums}
/* It sits inside a sentence, so the primitive lift and press would shove a word
   around in a line of text. */
#login-resend:hover,#login-resend:active{transform:none}
/* Cooling down. Not the primitive opacity fade: at 12px, half-opacity accent
   text stops being readable, and the countdown is the one thing this state has
   to say. Dropping the underline and the accent carries "not yet" instead, and
   keeps its contrast. */
#login-resend[disabled]{opacity:1;color:var(--muted);cursor:not-allowed;text-decoration:none}
#login-error{font-size:var(--fs-caption);color:var(--red);min-height:16px}
/* Secondary exit, fenced off from the form it isn't part of. */
.login-foot{border-top:1px solid var(--border);padding-top:var(--sp-16);
  font-size:var(--fs-caption);color:var(--muted);text-align:center}
/* Its own line, so the link never wraps mid-phrase and orphans the arrow. */
#intake-link{display:block;margin-top:var(--sp-2);color:var(--s-text)}
#user-badge{margin-top:8px;display:flex;flex-direction:column;gap:6px;font-size:12px;color:var(--muted)}
#user-badge button{align-self:flex-start;padding:4px 13px;border:1px solid var(--b2);border-radius:var(--r-pill);background:var(--bg1);cursor:pointer;font-size:11px}
/* ── FILE DROP ──
   Rebuilt 2026-08-19. The two controls used to be raw UA widgets: a native
   <select> with an OS chevron and a near-default <button>, both a good deal
   smaller than any other control on the site. They now match the rest of the
   system — 32px tall like the PM modal's control row, a pill button, and the
   same custom-chevron select the board filters and the task modal use. */
.dropzone{border:1.5px dashed var(--b2);border-radius:var(--rl);padding:22px 16px;margin:14px 0 20px;
  text-align:center;color:var(--muted);background:var(--bg2);
  transition:border-color var(--d1),background var(--d1)}
.dropzone:hover{border-color:var(--b3)}
/* Drag feedback: js/dropzones.js toggles .dropzone-over on dragover/dragleave. */
.dropzone-over{border-color:var(--s);border-style:solid;background:var(--s2)}
.dropzone-disabled{opacity:.45;cursor:not-allowed}
.dropzone-disabled:hover{border-color:var(--b2)}
.dropzone-label{font-family:var(--fb);font-size:var(--fs-body-lg);font-weight:500;color:var(--text)}
.dropzone-sub{font-size:var(--fs-body-sm);color:var(--dim);margin-top:3px}
.dropzone-controls{display:flex;align-items:center;justify-content:center;gap:8px;margin-top:14px;flex-wrap:wrap}
/* Same custom chevron + surface as .board-filters select / .modal select, so
   File Drop stops being the one place a raw OS control shows through. */
.dropzone-type{appearance:none;-webkit-appearance:none;height:32px;padding:0 28px 0 12px;line-height:1.25;
  border:1px solid var(--b2);border-radius:var(--r-pill);background-color:var(--bg1);
  background-image:var(--sel-chevron);
  background-repeat:no-repeat;background-position:right 11px center;
  color:var(--text);font-family:var(--fb);font-size:var(--fs-body-sm);cursor:pointer;
  transition:border-color var(--d1),box-shadow var(--d1)}
.dropzone-type:focus{outline:none;border-color:var(--s);box-shadow:var(--ring)}
.dropzone-choose-btn{display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 18px;border:1px solid var(--s);border-radius:var(--r-pill);
  background:var(--s);color:var(--on-accent);cursor:pointer;
  font-family:var(--fb);font-size:var(--fs-body-sm);line-height:1;font-weight:600;
  transition:transform var(--spring-dur-1) var(--spring-1),box-shadow var(--d2) var(--ease-out)}
.dropzone-choose-btn:hover{box-shadow:var(--glow-accent);transform:translateY(-1px)}
.dropzone-choose-btn:active{transform:translateY(0) scale(.97)}
.dropzone-disabled .dropzone-choose-btn{cursor:not-allowed}
.dropzone-status{width:100%;font-size:var(--fs-caption);color:var(--muted);min-height:16px;margin-top:8px}
/* ── STAGED SUBMISSION ──
   Chosen files wait here so the batch can be pointed at a ticket before it goes
   anywhere (js/dropzones.js). A panel rather than more of the dashed zone: this
   is a form now, and it reads left-aligned even though the zone above it is
   centred. */
.dropzone-stage{width:100%;margin-top:14px;padding:12px;text-align:left;
  border:1px solid var(--b2);border-radius:var(--r);background:var(--bg1);box-shadow:var(--elev-tile)}
.dropzone-stage-hd{font-family:var(--fb);font-size:11px;font-weight:600;letter-spacing:var(--tr-md);
  text-transform:uppercase;color:var(--text)}
.dropzone-stage-help{margin:4px 0 0;font-size:var(--fs-caption);line-height:1.4;color:var(--dim)}
.dropzone-staged{list-style:none;margin:8px 0 0;padding:0;display:flex;flex-direction:column;gap:2px}
.dropzone-staged li{display:flex;align-items:center;gap:8px;font-size:var(--fs-body-sm);color:var(--text)}
/* The name is the flexible half: long export filenames truncate rather than
   pushing the remove button out of the panel. */
.dropzone-staged-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The cross is an SVG (CROSS_SVG, shared with the Upload activity rows), NOT the
   × character, and it must stay one: place-items centres a glyph's LINE BOX, and
   the multiplication sign's ink sits ~1.25px below that box's centre in every
   font the UA hands an unstyled <button> except Arial — so the × read visibly low
   in a 20px disc. There is no font-size here for the same reason: nothing in this
   button is text. */
.dropzone-staged-remove{width:20px;height:20px;flex:none;display:grid;place-items:center;
  border:1px solid var(--b2);border-radius:var(--r-pill);background:var(--bg2);color:var(--muted);
  cursor:pointer;transition:color var(--d1),border-color var(--d1)}
.dropzone-staged-remove:hover{color:var(--red);border-color:var(--red)}
.dropzone-ticket-row{margin-top:12px}
.dropzone-ticket-note,.dropzone-title-note{margin:12px 0 0;font-size:var(--fs-caption);line-height:1.45;color:var(--dim)}
.dropzone-ticket-note--warning{color:var(--text)}
.dropzone-ticket-retry{margin-top:8px}
.dropzone-ticket-lbl{display:block;font-size:var(--fs-caption);color:var(--muted)}
/* Full width, not a pill in the control row: a ticket label is a title plus a
   stage plus a due date, and it has to be readable to be pickable. */
.dropzone-ticket{appearance:none;-webkit-appearance:none;width:100%;height:32px;line-height:1.25;margin-top:5px;
  padding:0 28px 0 12px;border:1px solid var(--b2);border-radius:var(--r-pill);
  background-color:var(--bg2);background-image:var(--sel-chevron);
  background-repeat:no-repeat;background-position:right 11px center;
  color:var(--text);font-family:var(--fb);font-size:var(--fs-body-sm);cursor:pointer;
  transition:border-color var(--d1),box-shadow var(--d1)}
.dropzone-ticket:focus{outline:none;border-color:var(--s);box-shadow:var(--ring)}
/* The name field, offered when no ticket is attached. Deliberately the same
   width, height and radius as .dropzone-ticket above: it sits in the same slot
   and answers the same question ("what is this upload?"), so the two should not
   read as different kinds of control. A field, not a button, so it keeps the 8px
   radius fields use everywhere rather than the pill. */
.dropzone-title-row{margin-top:12px}
.dropzone-title{appearance:none;-webkit-appearance:none;width:100%;height:32px;line-height:1.25;margin-top:5px;
  padding:0 12px;border:1px solid var(--b2);border-radius:8px;
  background-color:var(--bg2);color:var(--text);
  font-family:var(--fb);font-size:var(--fs-body-sm);
  transition:border-color var(--d1),box-shadow var(--d1)}
.dropzone-title::placeholder{color:var(--dim)}
.dropzone-title:focus{outline:none;border-color:var(--s);box-shadow:var(--ring)}
.dropzone-stage-actions{display:flex;align-items:center;gap:8px;margin-top:12px}
.dropzone-submit-btn{display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 18px;border:1px solid var(--s);border-radius:var(--r-pill);
  background:var(--s);color:var(--on-accent);cursor:pointer;
  font-family:var(--fb);font-size:var(--fs-body-sm);line-height:1;font-weight:600;
  transition:transform var(--spring-dur-1) var(--spring-1),box-shadow var(--d2) var(--ease-out)}
.dropzone-submit-btn:hover{box-shadow:var(--glow-accent);transform:translateY(-1px)}
.dropzone-submit-btn:active{transform:translateY(0) scale(.97)}
.dropzone-clear-btn{display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 16px;border:1px solid var(--b2);border-radius:var(--r-pill);
  background:var(--bg2);color:var(--muted);cursor:pointer;
  font-family:var(--fb);font-size:var(--fs-body-sm);line-height:1;
  transition:border-color var(--d1),color var(--d1)}
.dropzone-clear-btn:hover{border-color:var(--b3);color:var(--text)}
.dropzone-submit-btn[disabled],.dropzone-clear-btn[disabled]{opacity:.5;cursor:not-allowed;transform:none}
/* Three zones now (Graphics · Videos · Other), so they sit side by side on a wide
   screen instead of stacking into six near-identical full-width bands. */
.upload-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-16)}
.upload-dept{display:flex;align-items:center;gap:7px;font-family:var(--fb);font-size:11px;font-weight:600;letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--text)}
.upload-dept .icon{width:14px;height:14px}
.upload-item .dropzone{margin:6px 0 0}
/* The per-file ceiling, said once under the three zones rather than repeated in
   each. Sits between the zones and the log: the last thing read before dropping. */
.upload-limit{margin:var(--sp-16) 0 0;font-size:13px;color:var(--muted);line-height:1.6}
/* ── Spec table + notes (replaced the two .wf-card columns, 2026-09-05) ───────
   Rationale is in src/sections/uploads.html next to the markup. The short version:
   the five rows are the five things this page accepts, so the answer to "does my
   file meet spec" is one glance rather than 514 words of prose.
   Built on the shared .nc-table primitive (css/primitives.css) — this block only
   adds what is specific to a reference table of specs: column widths that stop
   Format and Ceiling from being squeezed by the long last column, top-aligned
   cells (rows are 1–3 lines tall and a vertically-centred short cell floats away
   from its row), and the draft/final badge. */
.upload-spec-lede{margin:var(--sp-24) 0 var(--sp-12);font-size:13px;line-height:1.7;
  color:var(--muted);max-width:92ch}
.upload-spec-lede strong{color:var(--text)}
/* The table's own box, and the query container for the stacked form below. The
   guide's content column is the viewport minus a 268px rail above 920px and the
   whole viewport below it, so viewport width does not order table width at all:
   measured, a 1100px viewport gives the table 752px (rail present) while a 900px
   one gives it 820px — WIDER at the narrower viewport. No @media threshold can
   describe that; @container asks the box itself. Same lesson as pm/analytics. */
.upload-spec-wrap{container-type:inline-size}
/* Percentages, not rem: fixed rem columns held their size as the container shrank
   and took the last column down to 36px at a 620px content width, which ran the
   section to 4400px tall. These scale instead. */
.upload-spec{table-layout:fixed}
.upload-spec th:nth-child(1),.upload-spec td:nth-child(1){width:21%}
.upload-spec th:nth-child(2),.upload-spec td:nth-child(2){width:21%}
.upload-spec th:nth-child(3),.upload-spec td:nth-child(3){width:8%}
.upload-spec td{vertical-align:top;line-height:1.6;color:var(--muted)}
/* The three identifying columns are the answer; the last one is the caveat. */
.upload-spec td:nth-child(1),.upload-spec td:nth-child(2),.upload-spec td:nth-child(3){color:var(--text)}
.upload-spec td:nth-child(1){font-weight:500}
.upload-spec td .icon{width:14px;height:14px;color:var(--dim);vertical-align:-2px;margin:0 5px 0 2px}
.upload-spec-badge{display:inline-block;min-width:3.4em;text-align:center;font-family:var(--fb);
  font-size:var(--fs-micro);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  padding:1px 7px;border-radius:var(--r-pill);background:var(--bg3);color:var(--muted)}
/* Dharma marks the finals because they are the terminal state, and the badge is
   the one place on this page an accent earns its keep. --on-accent, not --bg:
   those two flip per mode (see css/tokens.css). */
.upload-spec-badge--final{background:var(--s);color:var(--on-accent)}
.upload-notes{list-style:none;margin:var(--sp-16) 0 0;padding:var(--sp-12) var(--sp-16);
  display:grid;gap:var(--sp-8);background:var(--bg2);border:1px solid var(--border);border-radius:var(--rl)}
.upload-notes li{display:flex;gap:var(--sp-10);align-items:flex-start;
  font-size:13px;line-height:1.65;color:var(--muted)}
.upload-notes .icon{width:14px;height:14px;flex:none;margin-top:3px;color:var(--dim)}
.upload-notes strong{color:var(--text)}
/* nowrap by default, because a HYPHEN is a natural line-break opportunity and
   every code here is a suffix or a filename: `-FINAL` broke after the dash and
   rendered as "-" ending one line and "FINAL" starting the next, which reads as a
   typo in the very string people are meant to copy. No overflow-wrap value fixes
   that — the break is legal, not forced — so wrapping has to be switched off. */
.upload-notes code{font-size:12px;padding:1px 5px;border-radius:4px;background:var(--bg3);color:var(--text);
  white-space:nowrap}
/* …except the one full-length filename, which is 277px and overflowed the strip
   by 55px at a 360px viewport with nowrap on. A long name breaking between its
   segments reads fine — it is the short suffixes above that could not survive it —
   so this is opt-IN rather than the default. */
.upload-notes code.upload-code-wrap{white-space:normal;overflow-wrap:break-word}
/* ── Sent from this browser ──────────────────────────────────────────────────
   Lasting confirmation, which this page had none of: the progress row vanished
   on completion and Box was the only way to check. Deliberately quiet — it is
   reassurance, not a feature to draw the eye. */
.upload-recent{margin-top:var(--sp-24);padding-top:var(--sp-16);border-top:1px solid var(--border)}
.upload-recent[hidden]{display:none}
.upload-recent-h{margin:0 0 var(--sp-10);font-family:var(--fb);font-size:11px;font-weight:600;
  letter-spacing:var(--tr-md);text-transform:uppercase;color:var(--dim)}
.upload-recent-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--sp-6)}
.upload-recent-item{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-12);
  flex-wrap:wrap;font-size:13px;padding:var(--sp-6) var(--sp-10);background:var(--bg1);
  border-radius:var(--r)}
/* min-width:0 so a long filename ellipsises instead of pushing the meta off. */
.upload-recent-name{flex:1 1 auto;min-width:0;color:var(--text);overflow-wrap:anywhere}
.upload-recent-meta{flex:0 0 auto;font-size:11px;color:var(--dim);white-space:nowrap}
@media(max-width:900px){.upload-grid{grid-template-columns:1fr}}
/* Under ~720px of CONTAINER a four-column spec table cannot hold its measure, so
   it stops being a table: each row becomes a labelled block, headers come from the
   data-label on every <td>. The primitive's own per-cell border is dropped in
   favour of one border per row, otherwise the four internal rules read as four
   rows. `display:block` on the table elements is what un-tables it; the .nc-table
   rules that set colour and type still apply, which is the point of doing this
   here rather than with a second component. */
@container (max-width:720px){
  .upload-spec,.upload-spec tbody,.upload-spec tr,.upload-spec td{display:block;width:auto}
  /* ⚠️ The percentage widths above are `.upload-spec td:nth-child(N)` (0,2,1) and
     beat the `.upload-spec td` (0,1,1) `width:auto` on the line before, so without
     this every stacked cell stayed at 21% of the strip — 76px at a 430px viewport,
     which broke "Multi-page" across two lines and put the badge on its own row.
     Restated at matching specificity, later in the file, so source order wins. */
  .upload-spec td:nth-child(1),.upload-spec td:nth-child(2),.upload-spec td:nth-child(3){width:auto}
  .upload-spec thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .upload-spec tr{padding:var(--sp-12) 0;border-bottom:1px solid var(--b2)}
  .upload-spec tr:last-child{border-bottom:0}
  .upload-spec tbody tr:hover td{background:none}
  .upload-spec td{padding:var(--sp-4) 0;border-bottom:0}
  .upload-spec td::before{content:attr(data-label);display:block;font-family:var(--fb);
    font-size:var(--fs-micro);letter-spacing:var(--tr-md);text-transform:uppercase;
    color:var(--dim);margin-bottom:2px}
  /* The first cell already reads as a heading — badge, icon and name — so a
     "SENDING" label above it is noise at exactly the width with none to spare. */
  .upload-spec td:nth-child(1)::before{display:none}
  .upload-spec td:nth-child(1){font-size:var(--fs-body);padding-bottom:var(--sp-6)}
}

/* ══════════ FRONT DOOR (Phase 1 · G1) — kinetic login + hero ══════════
   Uses the Phase-0 foundation (nc-ambient, --fx-*, --elev-*, --spring-*, --ring).
   All entrances are killed by the global prefers-reduced-motion rule above, and
   the ambient mesh only animates once motion.js adds .nc-ambient--live. */

/* The Sinh Santano intro (the inline splash in src/shell.html) holds this
   screen's staged entrance at frame 0 while it plays, and releases it as its
   field dissolves, so the card rises into view as the veil lifts instead of
   finishing unseen behind an opaque overlay. animation-fill-mode:both means a
   paused animation sits on its `from` frame, i.e. the content is invisible
   until the class comes off — the controller therefore schedules removal
   unconditionally and up front. The universal selector keeps this correct as
   the screen gains elements; on ~40 nodes the cost is nil. */
html.akshardham-intro #login-screen *,
html.akshardham-intro #login-screen *::after{animation-play-state:paused}

/* login: full-screen canvas takeover with a stronger, cinematic ambient bloom.
   Opacities halved 2026-08-19 (.20/.13/.09 -> .10/.07/.05, i.e. down to the same
   values as the base .nc-ambient in primitives.css). Those numbers were set against
   the Asmita cream canvas, where a warm bloom sat INSIDE the page's own hue and read
   as light. On the monochrome white canvas the identical gradients read as a peach
   wash covering the whole viewport — accent as background rather than as highlight,
   which is the one thing this palette is meant not to do. Dark mode was never the
   problem (orange on near-black reads as glow at any of these values); light is the
   constraint, and one value has to serve both.
   Those .10/.07/.05 figures are the history, not the current declarations — the
   banding pass below re-expressed each bloom as a fitted stop list whose peak is
   the same number after the blur that used to eat 20-27% of it. What renders is
   unchanged; only where the falloff is computed moved. */
#login-screen[data-surface="canvas"]{background:var(--fx-canvas);overflow:hidden}
/* Dark base, dropped by the extra mean lift the stronger dither below adds
   (measured on the rendered page: 4.7 of 255), so the COMPOSITE floor lands
   where --fx-canvas put it rather than going grey — 14.7 before, 14.6 after.
   Scoped here rather than changed in the token because the splash in
   src/shell.html mirrors --fx-canvas by hand and has no dither of its own to
   compensate for; it needs to match this surface's composite, which is the
   unchanged number. */
body.dark #login-screen[data-surface="canvas"]{
  background:radial-gradient(120% 90% at 50% -10%,#0f0f0f,#050505 55%)}

/* No filter:blur, and the gradients below carry the falloff instead. This is a
   banding fix, and the measurement is the whole argument: an element with a
   filter is rendered into its own 8-bit intermediate, blurred, and composited,
   so the ramp is quantised twice with a smoothing pass in between. Scanning the
   steepest part of the field and counting the single-value plateaus a ramp sits
   on (a plateau there is a staircase tread — the wider, the more it bands):

     shipped, grain off        mean tread 22.0px   worst 46px
     shipped as-is @1x         mean tread 13.3px   worst 43px
     shipped as-is @2x         mean tread 15.8px   worst 44px   <- Retina
     no blur + dither @1x      mean tread 10.2px   worst 28px
     no blur + dither @2x      mean tread 10.1px   worst 19px

   The 2x row is the point. The grain that ships today loses most of its effect
   at device-pixel-ratio 2 (its tile is upscaled, so its energy stops being at
   the pixel), which is where every Retina machine looks at this screen. Keeping
   the blur and only fixing the grain gets 2x to 16.4px — i.e. nowhere. The blur
   has to go for the fix to survive the display it is actually viewed on.

   Those figures are one bloom in isolation. On the real screen five overlapping
   blooms already break each other's treads up, so the honest end-to-end number
   is smaller — aggregated over twelve scan rows of the live page, plateaus wide
   enough to read as a band (>=20px) per row:

     before 2.75 -> after 1.33  @1x        before 3.83 -> after 1.92  @2x
     90th-percentile tread 15.0 -> 10.0px  and 18.0 -> 14.0px

   Halved, not eliminated. What is left is the ramp itself: 39 levels of warmth
   over ~700px is a boundary every 18px however it is dithered, and the only
   thing that removes those is a shallower wash, which is a design decision and
   not this change.

   Removing it is also free in two other ways: five blurred layers per frame
   become zero, and the falloff stops being a 60px absolute blur on a vmax-sized
   box, i.e. it stops changing shape with the viewport.

   The stop lists are FITTED, not invented. Each bloom was rendered alone on
   black at 1440x900 and its true blurred alpha profile scanned; the nine stops
   below are that profile resampled at eighths. Two things fall out of the
   measurement that a hand-written falloff would miss: blur costs 20-27% of the
   stated peak alpha (so .10 renders as .0797, and the new peaks are the old ones
   scaled by that), and the light reaches zero at 79-87% of the gradient's own
   radius rather than at the 62%/66% the old stops claimed.
   How close the fit lands: rendering both recipes statically side by side, with
   the same base and no dither on either, the field differs by a mean of 1.0
   (light) / 1.25 (dark) levels of 255, worst case 5 at one bloom's steepest
   edge. The falloff moved; the picture did not. */
#login-screen .nc-ambient span{filter:none}
#login-screen .nc-ambient span:nth-child(1){top:-24%;left:-14%;background:radial-gradient(circle,
  rgba(190,58,52,.0797) 0%,rgba(190,58,52,.0750) 9.8%,rgba(190,58,52,.0638) 19.6%,
  rgba(190,58,52,.0495) 29.5%,rgba(190,58,52,.0349) 39.3%,rgba(190,58,52,.0203) 49.1%,
  rgba(190,58,52,.0082) 58.9%,rgba(190,58,52,.0022) 68.7%,transparent 78.6%)}
#login-screen .nc-ambient span:nth-child(2){background:radial-gradient(circle,
  rgba(190,58,52,.0558) 0%,rgba(190,58,52,.0525) 9.8%,rgba(190,58,52,.0446) 19.6%,
  rgba(190,58,52,.0347) 29.5%,rgba(190,58,52,.0244) 39.3%,rgba(190,58,52,.0142) 49.1%,
  rgba(190,58,52,.0057) 58.9%,rgba(190,58,52,.0015) 68.7%,transparent 78.6%)}
#login-screen .nc-ambient span:nth-child(3){background:radial-gradient(circle,
  rgba(190,58,52,.0399) 0%,rgba(190,58,52,.0375) 9.8%,rgba(190,58,52,.0319) 19.6%,
  rgba(190,58,52,.0248) 29.5%,rgba(190,58,52,.0175) 39.3%,rgba(190,58,52,.0101) 49.1%,
  rgba(190,58,52,.0041) 58.9%,rgba(190,58,52,.0011) 68.7%,transparent 78.6%)}
/* A fourth bloom, and the reason there is one: three lit the corners and the
   middle, so the field read as a fixed triangle no matter how it moved. The
   fourth sits high-right, behind the card, and is deliberately the smallest —
   it is what gives the card's own edge a moving light to catch.
   Its own fit: a 60px blur on a 44vmax box is a wider gaussian relative to the
   box, so it cost 27% of the peak rather than 20%, and its stops run further. */
#login-screen .nc-ambient span:nth-child(4){top:-16%;left:auto;right:-18%;
  width:44vmax;height:44vmax;background:radial-gradient(circle,
  rgba(190,58,52,.0437) 0%,rgba(190,58,52,.0409) 10.9%,rgba(190,58,52,.0341) 21.8%,
  rgba(190,58,52,.0256) 32.7%,rgba(190,58,52,.0165) 43.5%,rgba(190,58,52,.0085) 54.4%,
  rgba(190,58,52,.0031) 65.3%,rgba(190,58,52,.0008) 76.2%,transparent 87.1%)}
/* The travelling highlight, and the only bloom here allowed to be brighter than
   the wash. Measured, because the alpha is the whole argument: with the four
   above as the entire background, the light canvas changes by 4 of 255 levels
   over fifteen seconds of drift — i.e. it animates, and nobody can see it.
   Orange at .06 behind blur(60px) simply has no room to move on white.
   The palette rule that halved those four (see above) is specifically about
   accent covering the WHOLE VIEWPORT — "accent as background rather than as
   highlight". So this is the highlight: a sixth the area of the others and
   three times the alpha, which is a moving light rather than a wash, and it is
   the one that makes the field read as alive. It crosses the composition on a
   29s circuit — slow enough to be calm, short enough that it has visibly moved
   in the few seconds anyone spends on this screen.
   This one carried blur(30px) rather than the shared 60, so its fit differs
   again: 22% of the peak, and a slightly tighter falloff. Its alpha is the only
   one here that is meant to read as a light rather than a wash, and the fitted
   .1569 is exactly what the shipped .20 rendered as once the blur had taken its
   cut — not a decision to dim it. */
#login-screen .nc-ambient span:nth-child(5){top:2%;left:16%;width:26vmax;height:26vmax;
  background:radial-gradient(circle,
  rgba(190,58,52,.1569) 0%,rgba(190,58,52,.1474) 10.6%,rgba(190,58,52,.1259) 21.3%,
  rgba(190,58,52,.0966) 31.9%,rgba(190,58,52,.0672) 42.5%,rgba(190,58,52,.0371) 53.2%,
  rgba(190,58,52,.0155) 63.8%,rgba(190,58,52,.0035) 74.4%,transparent 85.1%)}

/* Motion, not more colour. The alphas above are held exactly where #85 put them
   (see the note above: on a white canvas a warmer bloom stops being a highlight
   and becomes a peach background, which is the one thing this palette forbids),
   so everything below buys presence with movement instead.
   Three changes from the shared .nc-ambient--live drift in primitives.css:

   1. A path each, not one shared. nc-drift moves every blob along the same
      vector, so the whole field slid as a single sheet — which is exactly what
      a still image looks like. These pull against each other, so it churns.
   2. Closed loops (0% == 100%, infinite) rather than a two-stop `alternate`.
      An alternating drift retraces its own line and reads as a pendulum; a
      multi-stop loop circulates and never visibly turns around.
   3. Each bloom breathes in opacity as well as position. At these alphas a
      blob's *travel* is nearly invisible on white — the whole region getting
      brighter and dimmer is what the eye actually picks up.

   Durations are long and mutually non-multiple (37/43/53/61/29s) so the five
   never line back up into a pose you can catch repeating. Only transform and
   opacity animate, so this stays on the compositor. */
#login-screen .nc-ambient--live span:nth-child(1){animation:fd-mesh-a 37s var(--ease-in-out) infinite}
#login-screen .nc-ambient--live span:nth-child(2){animation:fd-mesh-b 43s var(--ease-in-out) infinite;animation-delay:-9s}
#login-screen .nc-ambient--live span:nth-child(3){animation:fd-mesh-c 53s var(--ease-in-out) infinite;animation-delay:-21s}
#login-screen .nc-ambient--live span:nth-child(4){animation:fd-mesh-d 61s var(--ease-in-out) infinite;animation-delay:-34s}
/* Linear, not eased: this one is a light crossing the room, and an ease at every
   quarter-turn would make it pause at four points on a circuit you can see. */
#login-screen .nc-ambient--live span:nth-child(5){animation:fd-mesh-e 29s linear infinite}
@keyframes fd-mesh-a{0%,100%{transform:translate3d(0,0,0) scale(1);opacity:.7}
  34%{transform:translate3d(9%,6%,0) scale(1.16);opacity:1}
  67%{transform:translate3d(2%,12%,0) scale(1.05);opacity:.82}}
@keyframes fd-mesh-b{0%,100%{transform:translate3d(0,0,0) scale(1.08);opacity:.85}
  34%{transform:translate3d(-11%,-5%,0) scale(1);opacity:1}
  67%{transform:translate3d(-4%,-11%,0) scale(1.14);opacity:.68}}
@keyframes fd-mesh-c{0%,100%{transform:translate3d(0,0,0) scale(1);opacity:.75}
  34%{transform:translate3d(13%,-9%,0) scale(1.2);opacity:1}
  67%{transform:translate3d(-7%,-4%,0) scale(1.06);opacity:.8}}
@keyframes fd-mesh-d{0%,100%{transform:translate3d(0,0,0) scale(1.1);opacity:.9}
  34%{transform:translate3d(-8%,11%,0) scale(1);opacity:.7}
  67%{transform:translate3d(-14%,4%,0) scale(1.18);opacity:1}}
/* Percentages are of this bloom's own 26vmax box, so 250% is ~65vmax — a circuit
   right across the composition and back rather than a nudge. Four stops, none of
   them retracing the last, so the path is a loop and not a line. */
@keyframes fd-mesh-e{0%,100%{transform:translate3d(0,0,0) scale(.9);opacity:.75}
  25%{transform:translate3d(160%,30%,0) scale(1.08);opacity:.95}
  50%{transform:translate3d(250%,140%,0) scale(1);opacity:.8}
  75%{transform:translate3d(60%,180%,0) scale(1.12);opacity:1}}

/* Re-assert the two pauses the five rules above silently switched back on. An
   `animation:` shorthand resets animation-play-state to running, and at
   #id .class span:nth-child() those rules outrank both
   `.nc-ambient--paused span` (motion.js, on tab-hide — without this the front
   door keeps compositing five blurred layers in a background tab) and the
   html.akshardham-intro hold that keeps this screen at frame 0 behind the splash.
   Written in the same shape so it matches their specificity and wins on order. */
#login-screen .nc-ambient--paused span:nth-child(n),
html.akshardham-intro #login-screen .nc-ambient span:nth-child(n){animation-play-state:paused}

/* Dither. Static, and the only non-accent layer on this canvas: a bloom this
   soft and this wide bands visibly on an 8-bit display, and noise is what makes
   the gradient read as a surface rather than as a gradient. Desaturated to grey
   (feTurbulence emits COLOUR noise without the feColorMatrix — coloured speckle
   on a monochrome canvas) so it never reads as the dirt a grey wash on white
   otherwise becomes.
   Not animated: film grain means repainting the whole viewport every frame, and
   the texture is doing its job standing still.
   Hung off .nc-ambient rather than off #login-screen so it lands inside the same
   already-positioned, already-clipped, z-index:-1 layer the blooms live in —
   over them, under the card, and with nothing to say about stacking.

   ONE octave, where this was three. Three octaves of fractalNoise is a
   low-frequency CLOUD, and a cloud cannot dither: to break a level boundary the
   noise has to swing across it within a pixel or two. Measured on a deliberately
   brutal ramp (22 levels over 1400px, so a boundary every ~64px), counting the
   plateaus wider than 20px left behind:

     no noise at all               25 bands
     3 octaves @ .05 (shipped)     10 bands, +4.5 of black lift
     1 octave  @ .05               11 bands, +4.5
     1 octave  @ .10                2 bands, +8.9

   The three-octave version was also paying 4.5 levels of black lift for that,
   i.e. washing the canvas out to buy almost nothing. The extra lift at .10 is
   handed back by the compensated dark base above, so the composite floor is
   unchanged and the blacks are no lighter than they are today.
   background-size pins the tile to CSS pixels; without it the 160px SVG was
   upscaled at devicePixelRatio 2 and its energy stopped landing at the pixel,
   which is most of why the shipped version does so much less on a Retina
   display than in a 1x screenshot. */
#login-screen .nc-ambient::after{content:"";position:absolute;inset:0;pointer-events:none;opacity:.022;
  background-size:180px 180px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.95' numOctaves='1' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E")}
/* Dark needs more of it, not less: the same 2% of grey speckle that reads as
   texture on #fafafa is most of the way to invisible on #0a0a0a. Light barely
   bands at all — its canvas ramp is five levels, and the eye cannot resolve a
   single sRGB step up there — so light keeps the amount it had. */
body.dark #login-screen .nc-ambient::after{opacity:.10}

/* The blob rules above are #id .class :nth-child, so they outrank the
   `.nc-ambient--live span{animation:none}` in primitives.css's reduced-motion
   block and have to opt out by name. In practice motion.js never adds
   .nc-ambient--live under reduced motion, so this is the case where the OS
   setting is flipped after the class is already on. */
@media (prefers-reduced-motion: reduce){
  #login-screen .nc-ambient span,#login-screen .nc-ambient--live span{animation:none}
}

/* mode toggle: chrome, so it sits in the canvas corner rather than in the card.
   .mode-toggle is position:relative; overriding to absolute keeps its knob and
   icons positioned against it, since it stays a positioned ancestor. */
.login-mode-toggle{position:absolute;top:var(--sp-20);right:var(--sp-20);z-index:2;
  animation:fd-rise var(--d3) var(--ease-out) .6s both}
@media (min-width:960px){.login-mode-toggle{top:var(--sp-32);right:var(--sp-32)}}

/* the stage: brand panel beside the card at >=960px, stacked below it.
   #login-screen itself stays a flex centering box — js/auth.js and the
   html.auth-out rule both set display:flex on it by hand. */
.login-stage{width:100%;max-width:1040px;display:grid;gap:var(--sp-32);justify-items:center;position:relative;z-index:1}
@media (min-width:960px){
  .login-stage{grid-template-columns:minmax(0,1fr) 400px;gap:var(--sp-72);align-items:center;justify-items:stretch}
}

/* brand panel — centred over the card on narrow screens, left-aligned beside it on wide */
.login-brand{max-width:440px;text-align:center}
/* 210px wide is 232px TALL at the lockup's 0.9071 — the artwork is portrait, so
   the width that used to give a modest crown now drives the height. Both
   treatments are the same artwork in two inks and share one ink box exactly
   (5349x5897), so declaring the ratio here cannot stretch either of them.
   The -320 derivative covers this and the intake and splash mastheads at 1x. */
.login-logo{width:min(210px,52vw);aspect-ratio:5349/5897;margin:0 auto var(--sp-20);
  background:url('/assets/logo/akshardham-lockup-charcoal-320.png') center/contain no-repeat;
  animation:fd-logo .9s var(--ease-out) both}
/* The only mode switch in this file, and the only one that can't be a token:
   the reversed lockup is a different FILE, not a different colour. Brand rule —
   the mark is never recoloured, you pick the treatment that contrasts.
   Named for the ink, so there is nothing to misread: charcoal #2D2926 on the
   light field, coconut #E5DECF on the dark one. */
body.dark .login-logo{background-image:url('/assets/logo/akshardham-lockup-coconut-320.png')}
/* Sized against the display face. An earlier pass on this branch scaled it to
   clamp(30px,4.4vw,56px) to compensate for Salitia's .498em cap height; Salitia
   was retired in #85 and that compensation was dropped rather than carried.
   NOT rescaled for Addington CF either (2026-09-26), and that is a decision:
   Addington's cap height is .630em against Cormorant's .625em — within half a
   percent — so the clamp still lands where it was measured. Its x-height is the
   part that moved (+14.0%), and x-height does not set this phrase's width. */
.login-brand-title{color:var(--text);font-family:var(--fd);font-weight:600;font-size:clamp(26px,3.2vw,38px);
  line-height:1.04;letter-spacing:-.01em;position:relative;padding-bottom:var(--sp-16)}
.login-brand-title::after{content:"";position:absolute;left:0;right:0;bottom:0;margin:0 auto;
  height:2px;width:56px;background:var(--s);transform-origin:center;animation:fd-sweep .8s var(--ease-out) .5s both}
/* The word wrapper that keeps a line break out of the middle of a word. Its
   children are inline-block, and an inline-block is a break opportunity, so
   without this the title could break between any two letters — "Multimedia
   Production Gu / ide" at 320px. inline-block here makes each word atomic, and
   igniteWordmark (script.js) emits ordinary spaces between these, which are then
   the only break points. Unscoped on purpose: both ignition sites want it. */
.ig-word{display:inline-block}
.login-brand-title .ig-ch{display:inline-block;white-space:pre;animation:fd-ignite .72s var(--ease-out) both;
  animation-delay:calc(.18s + var(--i) * .045s)}
/* 46ch holds this to three or four lines, so it is a passage rather than a
   paragraph and balance applies: it broke 61/60/34ch at 1440 and 50/45/41/18ch
   at 390, and sets 52/52/51 and 37/38/38/41 balanced. */
.login-brand-line{margin-top:var(--sp-20);max-width:46ch;color:var(--muted);font-size:var(--fs-body-lg);
  line-height:1.65;text-wrap:balance;animation:fd-rise var(--d3) var(--ease-out) .55s both}
@media (min-width:960px){
  .login-brand{max-width:none;text-align:left}
  /* 239px, not the 360px the landscape lockup took: at the new portrait ratio
     360 wide is 397 TALL, which overtops the 400px sign-in card beside it. 239
     lands at 264px tall — the taller of the two panels by a little, which is
     what a masthead should be, rather than by half again. */
  .login-logo{width:239px;margin:0 0 var(--sp-32)}
  .login-brand-title::after{margin:0;transform-origin:left}
}
/* Below the split the panel is a crown for the card, so it keeps only the mark
   and the title — the brand line would push the form off the first screen. */
@media (max-width:959px){
  .login-brand-line{display:none}
  .login-brand-title::after{width:44px}
}

/* card
   Dropped off --elev-4 on 2026-08-21. --elev-4 is the MODAL elevation, and a
   modal earns that weight from the dimmed backdrop behind it — it has to read as
   floating above a darkened page. This card floats above a near-white canvas
   with nothing dimmed, so the same shadow (.28 black, 60px blur reaching ~36px
   below the card) landed as a grey smear on white rather than as lift.
   --elev-card-hover is the "a card, lifted" weight and the reach is a third of
   it (~15px), which is what a 400px panel on an undimmed field wants. The warm
   --fx-accent-glow is what should be carrying the halo here, and at --elev-4 it
   was buried under the black one.
   --hairline goes with it, per the note at --elev-card in css/tokens.css: an
   inset white top edge does nothing on a white card and just muddies the top
   border. Dark mode needs no such cue from here — dark's --elev-card-hover
   already carries a brighter inset top highlight than the pair it replaces. */
.login-form{position:relative;z-index:1;
  box-shadow:var(--elev-card-hover),var(--fx-accent-glow);animation:fd-rise-lg var(--d3) var(--spring-2) both}
.login-sub{animation:fd-rise var(--d3) var(--ease-out) .30s both}
.login-form label{animation:fd-rise var(--d3) var(--ease-out) .38s both}
.login-form button{animation:fd-rise var(--d3) var(--ease-out) .46s both}
#login-restart,.login-resend-row,.login-foot{animation:fd-rise var(--d3) var(--ease-out) .54s both}
/* The resend button rides in on its row rather than on the rule above: two
   entrances on nested nodes make the sentence and the word inside it arrive
   separately. */
#login-resend{animation:none}
@keyframes fd-logo{from{opacity:0;transform:translateY(12px) scale(.97);filter:blur(6px)}to{opacity:1;transform:none;filter:blur(0)}}
@keyframes fd-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes fd-rise-lg{from{opacity:0;transform:translateY(30px) scale(.97)}to{opacity:1;transform:none}}
@keyframes fd-sweep{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes fd-ignite{from{opacity:0;transform:translateY(85%) rotate(1.5deg);filter:blur(5px)}to{opacity:1;transform:none;filter:blur(0)}}

/* hero: ambient bloom behind (masked into the page) + staged editorial entrance */
.hero{position:relative;isolation:isolate;overflow:hidden}
.hero > .nc-ambient{-webkit-mask-image:linear-gradient(to bottom,#000 50%,transparent);mask-image:linear-gradient(to bottom,#000 50%,transparent)}
.hero .nc-ambient span{width:40vmax;height:40vmax;filter:blur(52px)}
.ht-guj .ig-ch{display:inline-block;white-space:pre;animation:fd-ignite .7s var(--ease-out) both;animation-delay:calc(var(--i) * .04s)}   /* per-letter ignition, left-to-right; parent transform stays free for parallax */
.hk{animation:fd-rise var(--d3) var(--ease-out) both}
.ht-en{animation:fd-rise var(--d3) var(--ease-out) .05s both}
.hm{animation:fd-rise var(--d3) var(--ease-out) .12s both}
.hs{animation:fd-rise var(--d3) var(--ease-out) .18s both}
.hnums{animation:fd-rise var(--d3) var(--ease-out) .24s both}
.hst{animation:fd-rise var(--d3) var(--ease-out) .30s both}
@keyframes fd-reveal{from{opacity:0;clip-path:inset(0 0 100% 0)}to{opacity:1;clip-path:inset(0 0 0 0)}}

/* ══════════ SECTION CARDS (Phase 1 · G3) — tactile depth ══════════
   Base elevation + hover lift on every section card, via the shared selectors
   (touch-it-tokenize-it: no per-section HTML churn). Uses --elev-* so the depth
   reads in dark mode too — the old one-off rgba(0,0,0) card shadows vanished on black. */
.tc,.prog-card,.vakya-card,.rm-card,.sk-card,.cr-card,.wf-card{
  box-shadow:var(--elev-card);
  transition:transform var(--d1) var(--ease-out),box-shadow var(--d2) var(--ease-out),border-color var(--d1)}
.tc:hover,.prog-card:hover,.vakya-card:hover,.rm-card:hover,
.sk-card:hover,.cr-card:hover,.wf-card:hover{
  transform:translateY(-2px);border-color:var(--border-raised);box-shadow:var(--elev-card-hover)}
/* .tc.open stays on --elev-3: an expanded section card is a disclosure, not a
   resting tile, and it should read as clearly above the collapsed ones. */
.tc.open{box-shadow:var(--elev-3),var(--hairline)}

/* ══════════ AI STUDIO (Phase 1 · G4) — generative "develop-in" ══════════ */
/* in-flight: orange-tinted scan on the skeleton + a live pulsing ring on the badge */
.ai-skeleton-media::after{background:linear-gradient(90deg,transparent,var(--s2),transparent)}
.ai-status-processing{animation:ai-ring 1.5s var(--ease-out) infinite}
@keyframes ai-ring{0%{box-shadow:0 0 0 0 var(--s3)}70%{box-shadow:0 0 0 7px transparent}100%{box-shadow:0 0 0 0 transparent}}
/* result: the asset develops in — grayscale→colour, bottom-up clip reveal, scale settle */
.ai-asset-media{animation:ai-develop .8s var(--ease-out) both}
@keyframes ai-develop{
  from{opacity:0;transform:scale(1.05);filter:grayscale(1) contrast(1.15) brightness(1.08);clip-path:inset(0 0 100% 0)}
  60%{filter:grayscale(.3)}
  to{opacity:1;transform:none;filter:none;clip-path:inset(0 0 0 0)}}
/* completion: badge pop + one-shot orange ring ping */
.ai-status-pop{animation:ai-pop .55s var(--ease-out)}
@keyframes ai-pop{0%{transform:scale(1);box-shadow:0 0 0 0 var(--s3)}30%{transform:scale(1.18)}100%{transform:scale(1);box-shadow:0 0 0 11px transparent}}

/* ══════════ MOTION (last but one, deliberately) ══════════
   The BRIEF's situation→treatment table, applied to the guide. Everything here
   animates transform/opacity only, on the existing tokens — no new curves, no
   new durations. Placed near the end because every rule below reunifies a
   transition that a base rule further up already declared, and a `transition`
   shorthand REPLACES rather than extends, so source order is the whole
   mechanism. The reduced-motion escape is the file-wide
   `*{transition:none!important;animation:none!important}` above, plus the
   :active block at the very bottom of this section for the transforms it
   cannot reach. */

/* Section / view switch. display:none -> block restarts a CSS animation, and
   showView() toggles exactly that on every section, so the band replays its
   entrance each time you arrive — no JS re-trigger to keep in sync. The three
   lines arrive in reading order rather than as one slab. */
.nc-section-intro{animation:nc-section-in var(--d3) var(--ease-out) both}
.nc-section-intro .nc-pagehead-eye{animation:nc-section-line var(--d2) var(--ease-out) .08s both}
.nc-section-intro .nc-pagehead-title{animation:nc-section-line var(--d2) var(--ease-out) .14s both}
.nc-section-intro .nc-pagehead-sub,
.nc-section-intro-link{animation:nc-section-line var(--d2) var(--ease-out) .20s both}
@keyframes nc-section-in{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes nc-section-line{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* Grid population. --i is set per card by script.js's tagReveal(); the cap is
   the same 312ms as --nc-stagger-cap in css/primitives.css, because at 26ms a
   thirty-card grid otherwise finishes arriving nearly a second after it
   renders. Overrides the transition-delay that primitives' `transition`
   shorthand zeroes — same selector, later sheet. */
.nc-motion [data-reveal]{transition-delay:min(calc(var(--i,0) * 26ms),312ms)}

/* Press. Nine control families here transitioned colour but had no press state
   at all, so a tap on a filter pill read as nothing happening until the colour
   caught up. One transition list for the lot, ending in the spring the rest of
   the app presses on. */
.fc,.af-btn,.af-mandal,.arc-stat,.tpl-crumb,.tpl-view,.tpl-sellink,
.arc-delete-btn,.tpl-dl,.dropzone-staged-remove{
  transition:background var(--d1) var(--ease-out),color var(--d1) var(--ease-out),
             border-color var(--d1) var(--ease-out),box-shadow var(--d2) var(--ease-out),
             transform var(--spring-dur-1) var(--spring-1)}
.fc:active,.af-btn:active,.af-mandal:active,.arc-stat:active,.tpl-crumb:active,
.tpl-view:active,.tpl-sellink:active,.arc-delete-btn:active,.tpl-dl:active,
.dropzone-staged-remove:active{transform:scale(.97)}
/* .vr-btn and .vr-dot are deliberately absent: the arrow is centred with
   `transform:translateY(-50%)`, so a bare scale() here would drop it half its
   own height down the stage — and the dot's active state is a width change the
   rotator owns. */

/* A press is a transform, not a transition, so the file-wide reduced-motion
   rule above (which only neutralises `transition` and `animation`) does not
   reach it. */
@media(prefers-reduced-motion:reduce){
  .fc:active,.af-btn:active,.af-mandal:active,.arc-stat:active,.tpl-crumb:active,
  .tpl-view:active,.tpl-sellink:active,.arc-delete-btn:active,.tpl-dl:active,
  .dropzone-staged-remove:active,.nc-section-intro-link:hover,
  .nc-section-intro-link:active{transform:none}
}

/* ── PHONE TOUCH TARGETS (last, deliberately) ──
   css/primitives.css raises the floor for the shared controls; the handful
   below hard-code a height of their own, and their base rules are further down
   this file than the phone block above — a media query adds no specificity, so
   source order is what decides. Restating them at the end is the fix, and the
   end is also where a reader looks for "what changes on a phone". */
@media(max-width:640px){
  /* 16px on .dropzone-title specifically, not just the 44px height: Safari zooms
     the whole page when a text field under 16px takes focus. The select beside it
     is not a text-entry field and does not trigger it. */
  .dropzone-type,.dropzone-choose-btn,.dropzone-ticket,.dropzone-submit-btn,.dropzone-clear-btn{height:44px}
  .dropzone-title{height:44px;font-size:16px}
  .dropzone-staged-remove{width:44px;height:44px}
  .fc,.tpl-crumb,.af-btn,.af-mandal{min-height:44px}
  /* The rotator's dots are a 6px position readout on an 8px pitch, which on a
     phone is eight 6px targets 14px apart. Growing them and spreading them out
     is the half that has to happen in the layout; the invisible ::after over
     each one is the half that doesn't. It is 44px TALL but only as wide as the
     dot plus its gap — a flat 44px square would have every dot's target
     overlapping three of its neighbours', and the later sibling wins the hit
     test, so tapping dot 3 would select dot 5. */
  .vr-dots{gap:16px}
  .vr-dot{width:10px;height:10px;position:relative}
  .vr-dot.active{width:26px}
  .vr-dot::after{content:"";position:absolute;top:50%;left:50%;
    width:calc(100% + 14px);height:44px;transform:translate(-50%,-50%)}
}
