/* Nextstep4u — design system.
 *
 * Outfit is the UI face — a variable geometric sans on Google Fonts under the
 * SIL Open Font License, loaded as one file across wght 400..800 (ADR 0003,
 * fifth revision). It is the ONLY face the site loads for Latin text and for
 * numbers.
 *
 * THE WEIGHTS ARE PLAIN NUMBERS. 400 means 400; nothing here is compensating
 * for anything. An earlier face needed its scale shifted to look right and left
 * values like 780 and 790 behind — those are gone, and a weight that is not a
 * round step is now a mistake rather than a correction. The floor is 400 and
 * `scripts/check-font-weights.mjs` enforces it on every push.
 *
 * A note for exam content: Outfit draws lowercase `l` and capital `I` as
 * similar vertical strokes. They are not identical, but at body size they are
 * close, so number statement lists `(i), (ii), (iii)` or `1, 2, 3` rather than
 * a bare capital `I`. Option keys are A–D in the database, so the answer sheet
 * itself is unaffected. The digit `1` carries a flag and is unambiguous.
 *
 * Meera Inimai sets Tamil and ships weight 400 ONLY, so Tamil is never bold
 * anywhere — the rule at the bottom of the type section enforces that rather
 * than trusting every author to remember it. It stays because Outfit
 * carries no Tamil glyphs at all: dropping it would not make Tamil match the
 * rest of the page, it would hand Tamil to whatever the phone happens to have.
 *
 * Motion lives here too. Everything is transform/opacity, 120-420ms, and the
 * whole layer collapses under prefers-reduced-motion.
 */

/* ── tokens ─────────────────────────────────────────────────────────────── */
:root{
  --blue:#0265E0; --blue-fill:#0265E0; --blue-deep:#0150B4; --blue-bg:#EBF3FE; --blue-line:#C5DAF8; --blue-tint:#F7FBFF;
  --green:#00B696; --green-ink:#00947C; --green-deep:#00695C; --green-bg:#E4F7F3; --green-line:#A8E5D9;
  --amber:#FBBC04; --amber-ink:#B8860B; --amber-deep:#7A5A00; --amber-bg:#FFF7E0; --amber-line:#FBE49B; --amber-tint:#FFFDF5;
  --red:#D93843; --red-bg:#FDECED;

  --ink:#0D1420; --ink-2:#3D4756; --ink-3:#5A6675; --ink-4:#9AA4B2;
  --line:#E4E8EE; --line-2:#EEF1F6; --line-3:#D7DDE6;
  --page:#ffffff; --sunk:#F8FAFC; --chip:#F2F5F9; --shell:#F6F8FB;
  --night:#0D1420; --night-2:#171F2C; --night-3:#263243;

  --shadow-1:0 1px 3px rgba(13,20,32,.06);
  --shadow-2:0 4px 20px rgba(13,20,32,.07);
  --shadow-3:0 12px 34px rgba(2,101,224,.14);

  --r-sm:8px; --r-md:11px; --r-lg:16px; --r-xl:18px;
  --ui:'Outfit',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --ta:'Meera Inimai','Outfit',sans-serif;
  /* There is no --mono, and there is no monospaced face on this site.
     Numbers were set in Roboto Mono once; two faces on one screen read as two
     fonts -- a score in one and its label in another -- and the difference was
     most obvious while scrolling past a page of figures. The token was then
     aliased to the UI face, which fixed the look and left twenty-five rules
     still saying "font-family: mono" about text that was not.
     Outfit sets everything Latin, including every number. What makes
     a column of figures line up, and what stops a countdown shifting width as
     its digits change, is `font-variant-numeric:tabular-nums` -- which this
     face supports -- and not a monospaced family. */

  /* The brand amber as raw channels, so a glow or a tint can be built with
     rgba() without a second copy of the hex drifting away from --amber. */
  --amber-rgb:251,188,4;

  /* WhatsApp's greens. Not ours and not from the canvas file: they exist here
     only so a WhatsApp control looks like WhatsApp, which is the entire reason
     that channel converts.

     THESE ARE THE LOGO'S OWN GREENS. The pair before them was sampled off a
     piece of supplied artwork and came out a purer, more acid green than the
     mark anybody actually recognises -- the button read as "a green button"
     rather than as WhatsApp, which is the one thing it had to do. --wa is the
     green of the logo tile; --wa-deep is WhatsApp's own darker green, used
     here for the foot of the gradient and the border. (The retired pair is
     named in scripts/check-palette.py, which is not scanned for colours, so
     writing it here would fail the very check that records it.)

     --wa-cta and --wa-cta-deep are the BUTTON's surface, and they are teal
     rather than green on purpose. The owner wanted the white-on-green button
     WhatsApp themselves use, and white on the logo green is 1.98:1 -- not a
     preference, a label a good number of people cannot read. WhatsApp's own
     palette has a teal half as well, and it is dark enough to carry white:
     4.51:1 at the top of the gradient and 7.67:1 at the foot, so the label
     clears 4.5:1 across the whole fill. --wa-cta-deep is their dark teal
     exactly; --wa-cta is a shade of their lighter teal, lowered until white
     cleared the floor.

     So the two pairs are not interchangeable. A surface IMITATING WhatsApp --
     the message bubbles in the phone mock -- is green with dark text, because
     that is what a WhatsApp message looks like. A BUTTON that opens WhatsApp
     is teal with white text.

     NOTHING that is not a WhatsApp affordance may use any of them --
     scripts/check-palette.py says the same thing where it allows them. */
  --wa:#25D366; --wa-deep:#1DA851;
  --wa-cta:#11857A; --wa-cta-deep:#075E54;
  --wa-rgb:37,211,102; --wa-cta-rgb:17,133,122;

  --ease:cubic-bezier(.2,0,0,1);
  --spring:cubic-bezier(.34,1.3,.64,1);
}

/* NO DARK MODE.
 *
 * The dark token set was removed deliberately, not lost. This product is
 * always light: one surface, one set of contrast ratios, one thing to audit.
 * The navy panels -- the report card, the WhatsApp band, the footer -- are
 * DESIGN surfaces built from --night, not a theme. They stay navy in every
 * setting, which is why they are defined above with the other tokens rather
 * than here.
 *
 * If dark mode ever comes back it is a deliberate piece of work, not a
 * media query pasted back in: every pairing has to clear its WCAG threshold
 * again, and the exam surface has its own Paper/Night palette that is
 * separate from the site theme on purpose.
 */

/* ── base ───────────────────────────────────────────────────────────────── */
*{box-sizing:border-box;margin:0;padding:0;}
html{-webkit-text-size-adjust:100%;}
body{
  background:var(--page);color:var(--ink);font-family:var(--ui);
  /* Declared, rather than left to the browser's default of 400.
     It is the same number, so this changes nothing today -- it is here because
     the day the body weight needs to move, an undeclared default is invisible:
     there is no line to find, no line to grep, and nothing for a sweep to
     remap. That happened once already and every sentence on the site was the
     last thing to be noticed. Tamil is unaffected either way; the .ta rule
     below pins it to 400 with !important, because Meera Inimai ships one
     weight and anything else is a synthesised bold. */
  font-weight:400;
  font-size:16px;line-height:1.6;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
a{color:var(--blue);text-decoration:none;transition:color .14s var(--ease);}
a:hover{color:var(--blue-deep);}
button{font-family:inherit;cursor:pointer;border:0;background:none;color:inherit;}
input,select,textarea{font-family:inherit;font-size:inherit;color:inherit;}
/* The browser's own stylesheet hands <code>, <pre>, <kbd> and <samp> a
   monospaced face, which is the one family this site does not have. The admin
   on superadmin.nextstep4u.in names a database table in running prose, and
   that one word arrived in Courier on a page that is otherwise Outfit
   Flex. Inheriting keeps the semantics -- it is still marked up as code --
   without introducing a second Latin face to say so. */
code,kbd,samp,pre{font-family:inherit;font-size:inherit;}
img,svg{display:block;max-width:100%;}
/* The `hidden` attribute has to beat a class that sets `display`, or it does
   nothing at all. Hiding the filter row on tests.html -- a .row, which is
   display:flex -- left it on screen, and the same would be true of any
   component hidden by attribute anywhere on the site. v1.css carried this
   rule for the pages that load it; it belongs here, with the rest of the
   reset, for the ones that do not. */
[hidden]{display:none !important;}
:focus-visible{outline:2.5px solid var(--blue);outline-offset:2px;border-radius:4px;}
::selection{background:var(--blue-bg);color:var(--ink);}
::-webkit-scrollbar{width:11px;height:11px;}
::-webkit-scrollbar-thumb{background:var(--line-3);border-radius:8px;border:3px solid transparent;background-clip:padding-box;}
::-webkit-scrollbar-track{background:transparent;}

/* ── type ───────────────────────────────────────────────────────────────── */
h1{font-size:clamp(1.9rem,1.3rem+2.2vw,3.1rem);line-height:1.08;letter-spacing:-.025em;font-weight:700;text-wrap:balance;}
h2{font-size:clamp(1.45rem,1.1rem+1.3vw,2.13rem);line-height:1.15;letter-spacing:-.02em;font-weight:700;text-wrap:balance;}
h3{font-size:1.19rem;line-height:1.35;letter-spacing:-.01em;font-weight:600;}
p{text-wrap:pretty;}
.lede{font-size:1.09rem;line-height:1.6;color:var(--ink-2);max-width:34em;}
.sub{color:var(--ink-3);font-size:.94rem;}
.dim{color:var(--ink-4);}
.eyebrow{font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-4);}
.m{font-variant-numeric:tabular-nums;font-feature-settings:'tnum' 1;}
.num{font-variant-numeric:tabular-nums;letter-spacing:-.02em;}

/* ── a launch offer, said three different ways ──────────────────────────
   Three pieces of information, three colours, none of them borrowed from
   another job:

     the price you pay       --ink      the plainest thing on the card
     the price you are not   --ink-3    struck, smaller, clearly secondary
     what you save           green      green reads as "saving" in India
     when it ends            amber      the one thing amber is good at here

   Red is deliberately absent. On a price it reads as an error or a warning,
   and the sale bar at the top of the page is already the only red on this
   site; two reds arguing about which one is the offer is worse than one.

   The struck figure is a price this site actually charges and returns to on
   a stated date, not an invented "original" that nothing was ever sold for.
   That distinction is the whole of the CCPA guidance on reference prices,
   and it is also just the About page's promise applied to a number.

   These live here rather than in v1.css because batch.html and checkout.html
   show the price too and load app.css alone. */
.v1-was{
  font-variant-numeric:tabular-nums;
  font-size:24px;font-weight:500;color:var(--ink-3);letter-spacing:-.02em;
  text-decoration:line-through;text-decoration-thickness:2px;
}
.v1-save{
  display:inline-block;align-self:flex-start;margin-top:12px;
  background:var(--green-bg);color:var(--green-deep);
  border-radius:999px;padding:5px 12px;
  font-size:12.5px;font-weight:600;font-variant-numeric:tabular-nums;
}
.v1-offer-note{
  display:block;margin-top:10px;
  background:var(--amber-bg);color:var(--amber-deep);
  border:1px solid var(--amber-line);border-radius:var(--r-sm);
  padding:9px 12px;font-size:13.5px;line-height:1.5;font-weight:500;
}

/* Tamil. Meera Inimai has ONE weight, so any bold here would be a browser
   synthesising it — which looks broken beside real Outfit bold.
   Hierarchy in
   Tamil comes from size and colour instead. */
.ta,[lang="ta"]{font-family:var(--ta);font-weight:400!important;line-height:1.8;}

/* ── layout ─────────────────────────────────────────────────────────────── */
.wrap{max-width:1200px;margin:0 auto;padding:0 32px;}
.wrap.tight{max-width:1000px;}
.wrap.slim{max-width:840px;}
.sec{padding:76px 0;}
.sec.sm{padding:48px 0;}
.row{display:flex;align-items:center;gap:14px;}
.row.top{align-items:flex-start;}
.spacer{flex:1;}
.grid{display:grid;gap:16px;}
.g2{grid-template-columns:repeat(2,minmax(0,1fr));}
.g3{grid-template-columns:repeat(3,minmax(0,1fr));}
.g4{grid-template-columns:repeat(4,minmax(0,1fr));}
.stack{display:flex;flex-direction:column;gap:12px;}
/* A field that runs the full width of a two-column form. As a class rather
   than an inline grid-column, so the phone rule below can cancel it: a child
   spanning two columns of a grid that has been collapsed to one column makes
   the browser invent a second column, and the form silently stays two-up. */
.g2 > .wide{grid-column:span 2;}

/* ── site chrome ────────────────────────────────────────────────────────── */
.topbar{
  background:var(--night);color:#fff;font-size:.85rem;font-weight:600;
  padding:10px 32px;display:flex;align-items:center;justify-content:center;gap:14px;flex-wrap:wrap;
}
.topbar a{color:var(--amber);}
header.site{
  position:sticky;top:0;z-index:40;background:var(--page);
  background:color-mix(in srgb,var(--page) 92%,transparent);
  backdrop-filter:blur(10px);border-bottom:1px solid var(--line);
}
header.site .inner{max-width:1200px;margin:0 auto;padding:0 32px;height:68px;display:flex;align-items:center;gap:30px;}
/* The brand mark. An image of the real logo, with the CSS wordmark behind it
   as the fallback -- site.js unhides the letters only if the file fails to
   load. Height-constrained with width auto, so the artwork's aspect ratio is
   whatever it is and the header does not need redesigning when it changes. */
.mark{display:flex;align-items:center;flex:none;}
.mark-img{height:30px;width:auto;display:block;}
.mark-text{display:flex;align-items:baseline;gap:1px;font-weight:800;font-size:1.31rem;letter-spacing:-.035em;}
.mark-text[hidden]{display:none;}
.mark .b{color:var(--blue);} .mark .f{color:var(--amber);font-size:1.15em;} .mark .u{color:var(--green);}

/* Smaller in the footer than in the header, where it is the first thing on
   the page. */
footer.site .mark-img{height:26px;}

nav.main{display:flex;gap:26px;}
nav.main a{position:relative;font-size:.91rem;font-weight:500;color:var(--ink-2);padding:4px 0;}
nav.main a::after{
  content:"";position:absolute;left:0;right:0;bottom:-2px;height:2px;border-radius:2px;
  background:var(--blue);transform:scaleX(0);transform-origin:left;transition:transform .22s var(--ease);
}
nav.main a:hover{color:var(--blue);}
nav.main a:hover::after,nav.main a[aria-current="page"]::after{transform:scaleX(1);}
nav.main a[aria-current="page"]{color:var(--blue);font-weight:600;}

.theme{display:inline-flex;border:1px solid var(--line);border-radius:999px;padding:2px;background:var(--page);}
.theme button{font-size:.72rem;font-weight:600;color:var(--ink-4);padding:5px 11px;border-radius:999px;transition:background .16s,color .16s;}
.theme button[aria-pressed="true"]{background:var(--blue-fill);color:#fff;}

/* Language, built to look like a sibling of the theme switch rather than a
   feature bolted on. The Tamil label is set at 400 like all Tamil on the site;
   the weight difference against the EN label beside it is deliberate and is
   the reason the two are sized separately. */
.lang{display:inline-flex;border:1px solid var(--line);border-radius:999px;padding:2px;background:var(--page);}
.lang button{font-size:.72rem;font-weight:600;color:var(--ink-4);padding:5px 11px;border-radius:999px;transition:background .16s,color .16s;}
.lang button[lang="ta"]{font-family:var(--ta);font-weight:400;font-size:.84rem;padding:3px 11px;}
.lang button[aria-pressed="true"]{background:var(--blue-fill);color:#fff;}
.lang button:focus-visible{outline:2px solid var(--blue);outline-offset:2px;}
@media(max-width:820px){.theme{display:none;}}

footer.site{background:var(--sunk);border-top:1px solid var(--line);margin-top:auto;}
footer.site .cols{display:grid;grid-template-columns:1.5fr repeat(3,1fr);gap:40px;padding:52px 0 36px;}
footer.site h4{font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink);margin-bottom:12px;}
/* Scoped away from .mark: this rule used to hit the three spans inside the
   wordmark too, stacking the logo into a column at the foot of every page. */
footer.site .col > a,footer.site .col > span{display:block;font-size:.88rem;color:var(--ink-3);margin-bottom:9px;}
footer.site .col .mark{display:flex;margin-bottom:0;}
footer.site .col .mark span{display:inline;margin-bottom:0;font-size:inherit;color:inherit;}
/* The column rule above turns block-level children into stacked rows; the
   logo is not one of those. */
footer.site .col .mark-img{display:block;margin-bottom:0;}
footer.site .base{padding:0 0 36px;font-size:.79rem;color:var(--ink-4);}

/* ── buttons ────────────────────────────────────────────────────────────── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  min-height:46px;padding:12px 22px;border-radius:var(--r-md);
  font-size:.95rem;font-weight:600;text-align:center;white-space:nowrap;
  background:var(--blue-fill);color:#fff;border:1px solid var(--blue-fill);
  transition:background .16s var(--ease),transform .16s var(--spring),box-shadow .16s var(--ease),border-color .16s;
  box-shadow:0 1px 2px rgba(2,101,224,.28);
}
.btn:hover{background:var(--blue-deep);border-color:var(--blue-deep);color:#fff;transform:translateY(-1px);box-shadow:0 4px 14px rgba(2,101,224,.28);}
.btn:active{transform:translateY(0) scale(.985);}
.btn.ghost{background:var(--page);color:var(--ink);border-color:var(--line-3);box-shadow:none;}
.btn.ghost:hover{border-color:var(--blue);color:var(--blue);background:var(--page);}
.btn.soft{background:var(--chip);color:var(--ink);border-color:transparent;box-shadow:none;}
.btn.soft:hover{background:var(--blue-bg);color:var(--blue);}
.btn.gold{background:var(--amber);border-color:var(--amber);color:#0D1420;box-shadow:0 1px 2px rgba(251,188,4,.35);}
.btn.gold:hover{filter:brightness(.94);color:#0D1420;box-shadow:0 4px 14px rgba(251,188,4,.35);}
.btn.tint{background:var(--blue-bg);color:var(--blue);border-color:transparent;box-shadow:none;}
.btn.tint:hover{background:var(--blue-line);color:var(--blue-deep);}
.btn.danger{background:var(--red-bg);color:var(--red);border-color:transparent;box-shadow:none;}

/* WhatsApp. The one place the site is allowed colours that are not ours: an
   alert button that says WhatsApp has to look like WhatsApp, or it reads as a
   generic form and the whole reason the channel converts is lost. The greens
   are the logo's own, which is why they sit in the palette check under "brand"
   rather than under our greens.

   THE LABEL AND THE GLYPH ARE WHITE, on WhatsApp's teal rather than on their
   green. The owner asked twice for the white-on-green button WhatsApp use
   themselves, and dark-on-green was twice called unappealing -- which it is;
   it reads as a generic green button rather than as the app it opens.

   White on the logo green is 1.98:1, so that exact button cannot be copied:
   that number is not a preference, it is a label a good number of people
   cannot read, on the control that takes money. WhatsApp's palette has a teal
   half as well, and it is dark enough to carry white. On --wa-cta the label
   is 4.51:1 and on --wa-cta-deep it is 7.67:1, so it clears 4.5:1 across the
   whole fill -- which is what a gradient has to be checked against, not just
   its midpoint.

   The gradient rather than a flat fill, because a flat fill is not what the
   brand's own surfaces look like: they run light at the top to dark at the
   foot. border-color is the deep end so the edge resolves against a light
   page instead of glowing. */
.btn.wa{
  background:linear-gradient(180deg,var(--wa-cta) 0%,var(--wa-cta-deep) 100%);
  border-color:var(--wa-cta-deep);color:#FFFFFF;
  box-shadow:0 6px 18px rgba(var(--wa-cta-rgb),.34);
}
/* The hover deepens rather than lifts. Lifting a fill that is carrying white
   text walks it back toward the contrast floor the teal was chosen to clear,
   so the button answers with a stronger glow and a fill that gets darker --
   which is also what WhatsApp's own button does when you press it. */
.btn.wa:hover{
  background:linear-gradient(180deg,var(--wa-cta-deep) 0%,var(--wa-cta-deep) 100%);
  border-color:var(--wa-cta-deep);color:#FFFFFF;
  box-shadow:0 10px 26px rgba(var(--wa-cta-rgb),.46);
}
.btn.wa:active{
  background:linear-gradient(180deg,var(--wa-cta-deep) 0%,var(--wa-cta-deep) 100%);
  box-shadow:inset 0 2px 5px rgba(0,0,0,.22);
}
.btn.wa .wa-mark{width:20px;height:20px;flex:none;fill:currentColor;}
.btn.wa.sm .wa-mark{width:17px;height:17px;}
.btn.wa.lg .wa-mark{width:23px;height:23px;}
.btn.lg{min-height:54px;padding:16px 30px;font-size:1.03rem;border-radius:12px;}
.btn.sm{min-height:38px;padding:9px 16px;font-size:.85rem;border-radius:9px;}
.btn.wide{width:100%;}
.btn[disabled],.btn.off{background:var(--chip);color:var(--ink-4);border-color:var(--line);box-shadow:none;cursor:not-allowed;pointer-events:none;}

/* ── surfaces ───────────────────────────────────────────────────────────── */
.card{background:var(--page);border:1px solid var(--line);border-radius:var(--r-lg);padding:24px;}
.card.pad-sm{padding:18px;}
.card.lift{transition:border-color .2s var(--ease),box-shadow .2s var(--ease),transform .2s var(--ease);}
.card.lift:hover{border-color:var(--blue-line);box-shadow:var(--shadow-2);transform:translateY(-2px);}
.card.hero{border:2px solid var(--blue);box-shadow:var(--shadow-3);}
.card.gold{border:2px solid var(--amber);background:var(--amber-tint);}
.card.dark{background:var(--night);border-color:transparent;color:#fff;}
.card.dark .sub,.card.dark p{color:#9AA4B2;}
.panel{background:var(--page);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;}
.panel .head{padding:18px 22px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:14px;font-weight:600;}

.note{border-radius:var(--r-md);padding:16px 18px;font-size:.92rem;line-height:1.55;background:var(--blue-bg);border:1px solid var(--blue-line);}
.note b{display:block;margin-bottom:3px;}
.note.good{background:var(--green-bg);border-color:var(--green-line);color:var(--green-deep);}
.note.warn{background:var(--amber-bg);border-color:var(--amber-line);color:var(--amber-deep);}
.note.bad{background:var(--red-bg);border-color:transparent;color:var(--red);}

.tag{display:inline-flex;align-items:center;gap:6px;font-size:.74rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:4px 9px;border-radius:6px;background:var(--chip);color:var(--ink-3);}
.tag.blue{background:var(--blue-bg);color:var(--blue);}
.tag.green{background:var(--green-bg);color:var(--green-ink);}
.tag.amber{background:var(--amber-bg);color:var(--amber-deep);}
.tag.red{background:var(--red-bg);color:var(--red);}
.pill{display:inline-flex;align-items:center;gap:7px;font-size:.82rem;font-weight:500;padding:7px 14px;border-radius:999px;border:1px solid var(--line);color:var(--ink-2);background:var(--page);transition:background .16s,border-color .16s,color .16s;}
.pill.on{background:var(--ink);border-color:var(--ink);color:var(--page);font-weight:600;}
.pill:hover{border-color:var(--blue-line);}
.dot{width:7px;height:7px;border-radius:50%;background:var(--green);flex:none;}

/* ── stats ──────────────────────────────────────────────────────────────── */
.stat{background:var(--page);border:1px solid var(--line);border-radius:13px;padding:18px;}
.stat .v{font-size:1.6rem;font-weight:700;line-height:1;font-variant-numeric:tabular-nums;letter-spacing:-.025em;}
.stat .k{font-size:.78rem;color:var(--ink-3);margin-top:7px;}
.stat .d{font-size:.75rem;font-weight:600;margin-top:6px;}
.stat .d.up{color:var(--green-ink);} .stat .d.down{color:var(--red);} .stat .d.hold{color:var(--amber-ink);}

.bar{height:8px;border-radius:5px;background:var(--line-2);overflow:hidden;}
.bar i{display:block;height:100%;border-radius:5px;background:var(--blue);width:0;transition:width .9s var(--ease);}
.bar.split{display:flex;}
.bar.split i{border-radius:0;transition:width .9s var(--ease);}

/* ── tables ─────────────────────────────────────────────────────────────── */
.tbl{width:100%;border-collapse:collapse;font-size:.9rem;}
.tbl thead th{
  text-align:left;padding:12px 18px;background:var(--sunk);border-bottom:1px solid var(--line);
  font-size:.71rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-4);white-space:nowrap;
}
.tbl td{padding:14px 18px;border-bottom:1px solid var(--line-2);vertical-align:middle;}
.tbl tbody tr{transition:background .14s;}
.tbl tbody tr:hover{background:var(--sunk);}
.tbl tbody tr:last-child td{border-bottom:none;}
.scroll-x{overflow-x:auto;-webkit-overflow-scrolling:touch;}

/* ── forms ──────────────────────────────────────────────────────────────── */
.field{display:block;margin-bottom:16px;}
.field > span{display:block;font-size:.8rem;font-weight:600;margin-bottom:7px;}
.input{
  width:100%;min-height:48px;border:1px solid var(--line-3);border-radius:10px;
  padding:12px 15px;background:var(--page);transition:border-color .16s,box-shadow .16s;
}
.input:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px var(--blue-bg);}
.opt-row{
  display:flex;align-items:center;gap:14px;border:1px solid var(--line);border-radius:12px;
  padding:15px 17px;min-height:58px;cursor:pointer;background:var(--page);
  transition:border-color .16s var(--ease),background .16s var(--ease),transform .16s var(--spring);
}
.opt-row:hover{border-color:var(--blue-line);}
.opt-row.on{border:2px solid var(--blue);background:var(--blue-tint);padding:14px 16px;}
.radio{width:20px;height:20px;border-radius:50%;border:1.5px solid var(--line-3);flex:none;transition:border .16s;}
.opt-row.on .radio{border:6px solid var(--blue);background:var(--page);}
.switch{width:42px;height:24px;border-radius:12px;background:var(--line-3);padding:3px;display:flex;flex:none;transition:background .2s var(--ease);}
.switch i{width:18px;height:18px;border-radius:50%;background:#fff;transition:transform .2s var(--spring);}
.switch.on{background:var(--blue);}
.switch.on i{transform:translateX(18px);}

/* ── avatar ─────────────────────────────────────────────────────────────── */
/* The signed-in chrome that stood here is assets/dash-shell.css now; the
   avatar stayed because the public header and the checkout wear it too. */
.avatar{width:34px;height:34px;border-radius:50%;background:var(--blue-fill);color:#fff;font-size:.8rem;font-weight:700;display:flex;align-items:center;justify-content:center;flex:none;}

/* ── motion ─────────────────────────────────────────────────────────────── */
@keyframes rise{from{opacity:0;transform:translateY(14px);}to{opacity:1;transform:none;}}
@keyframes fade{from{opacity:0;}to{opacity:1;}}
@keyframes pop{from{opacity:0;transform:scale(.94);}to{opacity:1;transform:none;}}
@keyframes growY{from{transform:scaleY(0);}to{transform:scaleY(1);}}
@keyframes blink{0%,100%{opacity:1;}50%{opacity:.25;}}
@keyframes shimmer{from{background-position:-380px 0;}to{background-position:380px 0;}}
@keyframes drift{from{transform:translate3d(0,0,0);}to{transform:translate3d(0,-14px,0);}}

/* Reveal on scroll. JS adds .in when the element enters the viewport; without
   JS the element is simply visible, so nothing is ever hidden by a failure. */
.rv{opacity:0;transform:translateY(16px);transition:opacity .55s var(--ease),transform .55s var(--ease);transition-delay:var(--d,0ms);}
.rv.in{opacity:1;transform:none;}
.rise{animation:rise .5s var(--ease) both;animation-delay:var(--d,0ms);}
.pop{animation:pop .34s var(--spring) both;animation-delay:var(--d,0ms);}
.blink{animation:blink 1.8s infinite;}
.col-grow{transform-origin:bottom;animation:growY .55s var(--ease) both;animation-delay:var(--d,0ms);}
.float{animation:drift 4.5s ease-in-out infinite alternate;}
.skel{background:linear-gradient(90deg,var(--line-2) 25%,var(--chip) 50%,var(--line-2) 75%);background-size:760px 100%;animation:shimmer 1.5s infinite linear;border-radius:6px;}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;}
  .rv{opacity:1;transform:none;}
}

/* ── responsive ─────────────────────────────────────────────────────────── */
@media (max-width:980px){
  .g4{grid-template-columns:repeat(2,minmax(0,1fr));}
  .g3{grid-template-columns:1fr;}
  footer.site .cols{grid-template-columns:1fr 1fr;gap:28px;}
}
@media (max-width:720px){
  body{font-size:15px;}
  .wrap{padding:0 18px;}
  .sec{padding:48px 0;}
  .g2,.g4{grid-template-columns:minmax(0,1fr);}
  .g2 > .wide{grid-column:auto;}
  header.site .inner{padding:0 18px;height:58px;gap:14px;}
  nav.main{display:none;}
  .card{padding:18px;}
  footer.site .cols{grid-template-columns:1fr;}

  /* A .row is a horizontal line of things on a desktop and a column of them
     on a phone. Without this it stays one line and pushes the page sideways,
     which is where most of the horizontal scrolling on this site came from. */
  .row{flex-wrap:wrap;}

  /* Grids and flex items refuse to shrink below their content unless told
     to. This is what makes a single wide child -- a table, a long word --
     widen the whole column and then the whole page. */
  .card,.panel{min-width:0;}
  .grid > *,.row > *,.stack > *{min-width:0;}
}

/* Below this the header cannot hold the mark, the language toggle and two
   buttons at full size, so everything gives a little rather than one thing
   being dropped. Losing the login button on a phone is worse than a tighter
   one: a returning candidate arrives to sign in. */
@media (max-width:560px){
  header.site .inner{padding:0 14px;gap:9px;}
  .tblx{margin-inline:-2px;}
  .mark-img{height:26px;}
  .btn.sm{padding:9px 11px;font-size:.8rem;min-height:36px;}
  .lang button{padding:5px 8px;font-size:.68rem;}
  .lang button[lang="ta"]{padding:3px 8px;font-size:.78rem;}
}

/* An iPhone SE is 320px wide and still in use. Nothing is dropped here
   either -- the header just gives up its last few pixels of padding. */
@media (max-width:380px){
  header.site .inner{padding:0 10px;gap:7px;}
  .btn.sm{padding:8px 9px;font-size:.76rem;}
  .lang button{padding:5px 6px;font-size:.65rem;}
  .lang button[lang="ta"]{padding:3px 6px;font-size:.74rem;}
  .mark-img{height:24px;}
}

/* A wide table on a phone scrolls inside its own box rather than dragging
   the page with it. The markup wraps every table that declares a min-width
   in one of these; the column widths are the point of a table and squeezing
   them is worse than a sideways swipe. */
.tblx{overflow-x:auto;-webkit-overflow-scrolling:touch;}
.tblx > table{margin:0;}

/* ── the consent bar ────────────────────────────────────────────────────
   Built by assets/analytics.js, and only when a measurement id is actually
   configured. Bottom of the screen rather than a full-page interstitial: it
   is a question, not a gate, and the answer is remembered. */
.ns-consent{
  position:fixed;left:16px;right:16px;bottom:16px;z-index:80;
  max-width:520px;margin-left:auto;
  background:var(--night);color:#E6E9ED;
  border-radius:var(--r-lg);padding:18px 20px;
  box-shadow:0 20px 50px rgba(13,20,32,.35);
  display:flex;flex-direction:column;gap:12px;
  animation:nsConsent .3s var(--ease) both;
}
.ns-consent p{font-size:14px;line-height:1.55;margin:0;}
.ns-consent a{font-size:12.5px;font-weight:600;color:#94A0AE;}
.ns-consent a:hover{color:#FFFFFF;}
.ns-consent-row{display:flex;gap:10px;flex-wrap:wrap;}
.ns-consent button{
  min-height:44px;padding:0 18px;border-radius:999px;
  font-size:14px;font-weight:700;cursor:pointer;border:0;
}
.ns-consent-yes{background:var(--green);color:var(--night);flex:1;}
.ns-consent-no{background:var(--night-3);color:#E6E9ED;}
@keyframes nsConsent{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}

@media (prefers-reduced-motion:reduce){
  .ns-consent{animation:none;}
}

/* The private-jobs link sitting under the board filters on jobs.html. It
   looks like a filter because it is in that column, but it leaves the page,
   so it carries an arrow and never a checked state. */
.fl-link{display:flex;align-items:center;gap:10px;color:var(--blue);font-weight:600;}
.fl-link:hover{color:var(--blue-deep);}
.fl-link .box{border-color:var(--blue-line);background:var(--blue-bg);}
