/* Naveed Rashid — Gold account. Obsidian and bullion: plain, warm, human. */

/* ============================================================================
   THE TWO VOICES — both served from this origin, both under `default-src 'self'`
   There is no CDN and no third-party request anywhere on this site, so the faces
   are files in static/fonts and nothing else. Only the Latin ranges are carried
   (the interface is English and the figures are Arabic numerals); a name with an
   accent in it pulls the -ext file, and nothing else ever does.

     Inter            everything read as data: the balance, every table, every
                      label and every control. It is here for one reason above
                      the rest — its tabular figures are drawn on one width, so
                      a column of dirhams lines up digit under digit and a price
                      does not jiggle sideways when it ticks.
     Instrument Serif the desk's own name, and the few headings that carry it.
                      A high-contrast display serif used only at 19px and above,
                      where its hairlines are an asset instead of a risk.

   `font-display:swap` on both: the page is readable in the system face from the
   first frame and re-sets when the file lands — a statement is never blank while
   a font is in flight. size-adjust holds the fallback close to the real face, so
   that re-set moves as little as it can.
   Licence: SIL Open Font Licence 1.1 — static/fonts/OFL.txt.
   ============================================================================ */
@font-face{font-family:"Inter";font-style:normal;font-weight:400 800;font-display:swap;
  src:url(/fonts/inter-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD}
@font-face{font-family:"Inter";font-style:normal;font-weight:400 800;font-display:swap;
  src:url(/fonts/inter-latin-ext.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,
    U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,
    U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Instrument Serif";font-style:normal;font-weight:400;font-display:swap;
  src:url(/fonts/instrumentserif-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD}
@font-face{font-family:"Instrument Serif";font-style:normal;font-weight:400;font-display:swap;
  src:url(/fonts/instrumentserif-latin-ext.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,
    U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,
    U+2113,U+2C60-2C7F,U+A720-A7FF}

*{box-sizing:border-box;margin:0;padding:0}
:root{
  /* the identity — unchanged, and the only place these colours are written */
  --bg:#05070c; --panel:#0c111e; --line:#1b2437; --line2:#2a3854;
  --txt:#f0f3fa; --mut:#9aa8c2;
  --gold:#d9b45b; --gold2:#f0d48a; --golddark:#8a6a1f;
  --up:#37d188; --down:#ff7a70;
  --paper:#f7f4ec; --paper-txt:#1c1f2c; --paper-mut:#5c6172; --paper-line:#dcd6c6;

  /* ---- the metal, as a ramp --------------------------------------------
     A single flat gold is paint. Five steps of one hue is metal: the specular
     where the light hits, the two working tones, the deep body and the shadow
     it casts. Every gold surface below is built from these and nothing else —
     which is why a button, a chip and a hairline all read as the same alloy.
     --gold (the core) and --gold2 (the light) keep their names and their values:
     forty rules and every alpha wash in the sheet are written against them. */
  --gold-lit:#fdf3d6; --gold-hi:#f7e3ae; --gold-mid:#d9b45b;
  --gold-deep:#b08a3c; --gold-shade:#7d5f23;
  /* the light the metal throws — used as a shadow colour, never as a fill */
  --gold-glow:#d9b45b3d;

  /* ---- the elevation ladder --------------------------------------------
     Five surfaces, and each one is a measurable step above the last. The old
     sheet had them all within a hair of each other, which is precisely why the
     interface looked flat: a card and the page it sat on were the same black. */
  --lift1:#070910;   /* the deepest inset: an input, a code, a well  */
  --lift2:#090d17;   /* a recessed box inside a panel                */
  --lift3:#0b101c;   /* a low panel                                  */
  --lift4:#0e1524;   /* a card that has to come off the page         */
  --lift5:#131c30;   /* a hover, a control, the top of a gradient    */

  /* ---- the two voices --------------------------------------------------- */
  --ui:"Inter",-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --display:"Instrument Serif",Georgia,"Times New Roman",serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;

  /* ---- depth: four shadows, and every raised thing uses one of them -----
     Each is a pair — a tight contact shadow that sits the element on the page,
     and a wide soft one that gives it its height — plus a 1px inner highlight
     along the top edge, which is what makes a surface read as lit from above
     rather than simply filled. */
  --sh1:0 1px 2px #00000059, 0 2px 8px -2px #0000006b;
  --sh2:0 2px 4px #00000066, 0 10px 26px -10px #000000a6;
  --sh3:0 4px 10px -2px #00000073, 0 22px 50px -18px #000000b8;
  --sh4:0 8px 20px -6px #00000080, 0 40px 90px -28px #000000cc;
  --edge:inset 0 1px 0 #ffffff0f;     /* the lit top edge of a raised surface */
  --edge-soft:inset 0 1px 0 #ffffff0a;

  /* ---- one rhythm: spacing on the 4px grid, and nothing off it ----------
     Every gap, pad and margin in the interface below is one of these seven.
     (The printed statement keeps its own document metrics — see the note at
     the top of the paper section — because it is a replica of the PDF.) */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px;

  /* ---- one voice: five type levels, nothing in between ------------------
     t1 micro   · t2 small · t3 body · t4 strong · t5 display (the balance) */
  --t1:11px; --t2:13px; --t3:16px; --t4:19px; --t5:clamp(32px,6.2vw,46px);

  /* ---- corners + motion ------------------------------------------------- */
  --r1:8px; --r2:11px; --r3:16px; --r4:20px;
  --fast:140ms; --slow:180ms; --ease:cubic-bezier(.4,0,.2,1);
}
html,body{background:var(--bg);color:var(--txt);
  font:var(--t3)/1.5 var(--ui);
  -webkit-font-smoothing:antialiased}
/* Inter's own contextual alternates: a straight-sided `l`, a `1` that cannot be read
   as an `l`, and the disambiguated zero. On a screen where every other line is a
   figure, that is legibility, not typography for its own sake. */
body{font-feature-settings:"cv05" 1,"cv09" 1,"ss03" 1;font-optical-sizing:auto;
  letter-spacing:-.006em}

/* ============================================================================
   THE LIGHT IN THE ROOM
   The page is not a flat black rectangle: it is an obsidian surface with the
   desk's own light on it. Four layers, all of them in ONE fixed pseudo-element
   behind the content, so they cost one paint, never move when the page scrolls,
   and cannot touch the layout of a single element above them:

     1. a warm gold bloom off the top right — the light the metal is lit by
     2. a cold blue counter-light low on the left, which is what stops the black
        going muddy where the two meet
     3. a guilloché: two crossed hairline rasters at 2% — the engine-turned
        engraving on a share certificate or a banknote, at the strength where it
        is felt rather than seen. It is what the eye reads as "this is printed
        on something", and it is drawn by the browser from two gradients rather
        than carried as a file.
     4. film grain — a single tile of fractal noise at 3.5%, tiled. It takes the
        banding out of the two big gradients above (a 1400px gradient over a
        near-black quantises into visible steps on an 8-bit panel) and gives the
        surface a texture instead of a vacuum.

   `z-index:-1` + `pointer-events:none`: nothing here is ever hit-tested, focused,
   read aloud, or in the way. Print puts it out entirely — see the print block.
   ============================================================================ */
body::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:
    radial-gradient(1200px 620px at 88% -12%, #d9b45b1c, transparent 62%),
    radial-gradient(900px 540px at 4% 104%, #1c3a6b2e, transparent 70%),
    repeating-linear-gradient(58deg,  #ffffff05 0 1px, transparent 1px 7px),
    repeating-linear-gradient(-58deg, #ffffff04 0 1px, transparent 1px 7px),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.34'/%3E%3C/svg%3E"),
    var(--bg);
  background-size:auto,auto,auto,auto,160px 160px,auto;
  background-repeat:no-repeat,no-repeat,repeat,repeat,repeat,no-repeat}
/* the guilloché is a texture, not a pattern to be read: it fades out entirely on a
   phone, where a 7px raster on a 3x screen becomes moiré */
@media screen and (max-width:620px){
  body::before{background-image:
    radial-gradient(760px 420px at 92% -14%, #d9b45b1a, transparent 62%),
    radial-gradient(620px 380px at 0% 104%, #1c3a6b26, transparent 70%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.3'/%3E%3C/svg%3E");
    background-size:auto,auto,160px 160px;
    background-repeat:no-repeat,no-repeat,repeat}
}
/* what the cursor leaves behind when a figure is dragged over: the desk's gold,
   not the browser's blue */
::selection{background:#d9b45b3d;color:#fff8e4}
/* the scrollbars are part of the room. Thin, dark, and gold under the thumb —
   the two properties Firefox understands, and the shadow-parts webkit does. */
*{scrollbar-width:thin;scrollbar-color:#2a3854 transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:#22304f;border-radius:99px;
  border:2px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:#3d4f77;background-clip:content-box}
/* Every figure a customer can read or add up is tabular: the digits line up
   in a column, so two rows can be compared by eye and a price does not jiggle
   sideways when it ticks. (.num stays for the elements JS marks by hand.) */
.num,.hero-equity,.hero-meta,.hero-avail,.hero-cap,.acct-chip,.mkbar,.stat .v,
.ov b,.meta b,.cap-row span:last-child,.preview .main,.preview.big,.total-row b,
.j-ts,input[type=number],.otp-input,.sec-input,.adm-table td.num,
.st .r,th.r,td.up,td.down{font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1}
button{font-family:inherit}
/* Keyboard only: whoever is tabbing can see where they are. A mouse or a thumb never
   triggers :focus-visible, so nothing about the design changes for touch. */
:focus-visible{outline:2px solid var(--gold2);outline-offset:2px}
.btn:focus-visible,button.big:focus-visible,.mid:focus-visible,.x:focus-visible,.step:focus-visible{
  outline:2px solid var(--gold2);outline-offset:3px}
/* A button that is waiting on the server says so in its own label; this only keeps
   the colour from looking "live" while it is not. */
button:disabled{cursor:default;opacity:.62}
.busy{position:relative}
.step:disabled,button.big:disabled{opacity:.7}

/* ============ text for a screen reader, and nothing for the eye ============
   The same class the skip link, the loading announcements and the live region use:
   out of the flow (a 1px clipped box), so it takes no space and moves no pixel —
   which also means it can never appear in a screenshot or a printed sheet. */
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* ============ the skip link: the first thing a keyboard meets ============
   Off the top of the page until it is focused, so a mouse, a thumb and every
   screenshot see nothing at all; the moment Tab reaches it, it is a gold button
   over the header that jumps straight to the content. */
.skip-link{position:absolute;left:var(--s3);top:var(--s3);z-index:200;
  transform:translateY(-320%);background:var(--gold);color:#181203;font-weight:700;
  font-size:var(--t2);padding:var(--s2) var(--s4);border-radius:var(--r2);
  text-decoration:none;transition:transform var(--fast) var(--ease)}
.skip-link:focus{transform:none}

/* ============ the page's own closing line (a real <footer>, a real landmark) ============
   Who issued this and the way back to the top. It carries no figure of its own on
   purpose: everything a reader might quote lives in the document above, and a footer
   that repeats a balance is a second place for it to be wrong. */
/* the page closes on the same ruled line it opened on — a letterhead has two */
.app-foot{position:relative;max-width:1120px;margin:0 auto;padding:var(--s5) var(--s4) var(--s5);
  border-top:0;color:var(--mut);font-size:var(--t2);letter-spacing:.012em;
  display:flex;gap:var(--s2) var(--s5);flex-wrap:wrap;align-items:center;
  justify-content:space-between}
.app-foot::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:linear-gradient(90deg,transparent,#d9b45b36 20%,#ffffff12 50%,#d9b45b36 80%,transparent)}
.app-foot a{color:var(--gold2)}
.app-foot a:focus-visible{outline:2px solid var(--gold2);outline-offset:3px}
/* the sign-in card's closing line: the same words as the sheet's masthead, under a
   hairline, so the page that asks for the password says who is asking */
/* --- the page that is not here (404.html) ---
   The sign-in room with one card in the middle of it: the mark, the number, one plain
   sentence and the two ways out. Built from the sign-in page's own rules (.login-wrap
   lights the room, .login-card is the card) so it cannot drift from the front door. */
.err-wrap{display:grid;place-items:center;min-height:100vh;min-height:100svh;padding:var(--s6) var(--s4)}
.err-card{text-align:center}
.err-card header{padding-bottom:var(--s4)}
.err-card h1{font-family:var(--display);font-weight:400;font-size:30px;letter-spacing:-.01em;margin-top:var(--s2)}
.err-mark{margin:0 auto var(--s4)}
.err-code{font-family:var(--display);font-size:64px;line-height:1;color:var(--gold2);letter-spacing:.04em;
  text-shadow:0 0 28px #d9b45b40}
.err-actions{display:grid;gap:var(--s3);margin-top:var(--s2)}
.err-btn{display:flex;align-items:center;justify-content:center;min-height:48px;text-decoration:none;font-size:var(--t3)}
.login-card .err-btn.ghost{color:var(--txt)}
.lg-foot{border-top:1px solid var(--line);margin-top:var(--s5);padding-top:var(--s3);
  text-align:center;color:var(--mut);font-size:var(--t1);letter-spacing:.01em}

/* ============ desk gate: nothing administrative is drawn until the SERVER says so ============
   The desk's controls (deposit, withdraw, gold in/out, gold margin, opening balance, correct,
   put-back, the price setter, the console link and the "viewing as the desk" notice) all carry
   `desk` or `desk-only`, and every one of them is hidden HERE — in the stylesheet the browser
   parses before a single line of JavaScript runs. `body.is-desk` is added by /app.js and
   /admin.js only after GET /api/me answers role=admin from the session, so a customer's page
   never paints them, not even for one frame, and not at all if the script fails to load.

   The buttons additionally carry the `hidden` attribute in the HTML, and `!important` here
   beats any later `.open` a tampering visitor could force onto a modal. This is a curtain, not
   the lock: every one of these dialogs posts to a route that asks require_admin() for itself,
   so a forged curtain reaches a 403 and nothing else. */
body:not(.is-desk) .desk,
body:not(.is-desk) .desk-only,
body:not(.is-desk) #btn-console,
body:not(.is-desk) #btn-opening{display:none !important}
/* /admin.html uses the same gate: the header and the accounts table carry `desk`, so a
   customer who opens that address sees only this one plain line — and /admin.js then takes
   them to their own statement. */
body.is-desk #adm-gate{display:none}
/* ---- the masthead of the application itself --------------------------------
   A letterhead, not a toolbar: the mark, the name, and one ruled line under the
   whole row that says where the page begins. The line is drawn on the bar's own
   ::after so it spans the reading column exactly, and it fades out at both ends
   rather than stopping dead — a rule with hard ends reads as a border that failed
   to reach the edge. */
.appbar{position:relative;max-width:1120px;margin:0 auto;padding:var(--s4) var(--s4) var(--s3);
  display:flex;align-items:center;gap:var(--s2) var(--s5);flex-wrap:wrap}
.appbar::after{content:"";position:absolute;left:var(--s4);right:var(--s4);bottom:0;height:1px;
  background:linear-gradient(90deg,transparent,#d9b45b40 16%,#ffffff17 50%,#d9b45b40 84%,transparent)}
.brand{display:flex;align-items:center;gap:var(--s3);min-width:0;flex:0 1 auto}
/* the mark is a struck medallion, not a coloured box. Three things make it metal:
   a radial that lights it from over the left shoulder, a rim of the alloy, and a
   ring of the light it throws a few pixels past that rim. */
.logo{width:44px;height:44px;border-radius:50%;display:grid;place-items:center;flex:none;
  background:radial-gradient(circle at 34% 24%, #253254, #0b1120 76%);
  border:1px solid #d9b45b85;color:var(--gold2);
  font:400 21px/1 var(--display);letter-spacing:.04em;
  box-shadow:0 0 0 4px #d9b45b12, var(--sh1), inset 0 1px 0 #ffffff20}
.brand h1{font-size:var(--t4);font-weight:650;line-height:1.25;letter-spacing:-.018em}
.brand span{display:block;color:var(--mut);font-size:var(--t2);line-height:1.35}
.top-right{display:flex;gap:var(--s2);align-items:center;flex:0 0 auto}
.wrap{max-width:1120px;margin:0 auto;padding:var(--s2) var(--s4) 70px}

/* ---- the market strip: price, book and how fresh they are --------------
   One line, always in the same place, right under the desk's name: the price
   a trade will use, and an honest word about where it came from. `Live` means
   the feed wrote it within the last few minutes; anything older says so. */
/* ---- the market strip ------------------------------------------------------
   The one live thing on the page, so it is given an edge of its own rather than
   floating in the header: a shallow well with a hairline round it, the way a ticker
   sits in its own housing on a trading screen. It keeps every id, every element and
   every word it had. */
.mkbar{display:flex;align-items:center;gap:var(--s3);flex:0 1 auto;min-width:0;
  flex-wrap:wrap;font-size:var(--t2);color:var(--mut);
  padding:6px var(--s4);border-radius:999px;
  background:linear-gradient(180deg,#0b101c,#070b13);border:1px solid #1c2740;
  box-shadow:inset 0 1px 0 #ffffff08, inset 0 -8px 20px -14px #d9b45b40}
.mkbar-price{background:0 0;border:0;cursor:pointer;color:var(--gold2);
  font-size:var(--t4);font-weight:700;letter-spacing:-.022em;padding:var(--s1) 0;
  font-variant-numeric:tabular-nums;
  transition:color var(--fast) var(--ease),text-shadow var(--fast) var(--ease)}
/* the price is the one live figure on the page: on hover it does not change colour
   into something duller, it warms — the glow is the metal catching the light */
.mkbar-price:hover{color:var(--gold-lit);text-shadow:0 0 22px #d9b45b73}
.mkbar-price small{font-size:var(--t2);color:var(--mut);font-weight:500;margin-left:var(--s1)}
.mkbar-book{font-size:var(--t2);color:#c6d1e6}
.mk-state{display:inline-flex;align-items:center;gap:var(--s2);padding:var(--s1) var(--s3);
  border-radius:999px;border:1px solid var(--line2);background:#0b101c;
  font-size:var(--t2);font-weight:600;color:var(--mut);letter-spacing:.012em;
  box-shadow:var(--edge-soft)}
.mk-state .dot{width:8px;height:8px;border-radius:50%;background:currentColor;flex:none}
.mk-state.live{color:#7ce3ae;border-color:#37d18855;background:#0f1f19}
.mk-state.live .dot{box-shadow:0 0 0 0 #37d18888;animation:nrPulse 2.4s ease-out infinite}
.mk-state.stale{color:#f0c98a;border-color:#f0b35e55;background:#241d10}
/* "manual" = the desk sets this price by hand. Not named .desk on purpose: that class is
   the curtain gate (body:not(.is-desk) .desk{display:none}), and the chip must show. */
.mk-state.manual{color:var(--mut)}
@keyframes nrPulse{0%{box-shadow:0 0 0 0 #37d18877}70%{box-shadow:0 0 0 7px #37d18800}
  100%{box-shadow:0 0 0 0 #37d18800}}
.mk-sync{font-size:var(--t1);color:var(--mut);letter-spacing:.02em}

/* ============ buttons ============
   Every control on the site is one of three things, and each one is lit the same way
   it would be in a room: a gradient from a lighter top to a darker foot, a 1px inner
   highlight along the top edge where the light lands, and a shadow underneath that
   sits it on the page. A flat fill has no top and no bottom, which is exactly why the
   old row of buttons read as printed rectangles rather than as things to press. */
.btn{border:1px solid var(--line2);background:linear-gradient(180deg,#1a2338,#131a2b);
  color:var(--txt);
  padding:var(--s2) var(--s4);border-radius:var(--r2);cursor:pointer;font-size:var(--t2);
  font-weight:600;white-space:nowrap;letter-spacing:.004em;
  box-shadow:var(--edge-soft), var(--sh1);
  transition:background var(--fast) var(--ease),border-color var(--fast) var(--ease),
    transform var(--fast) var(--ease),color var(--fast) var(--ease),
    box-shadow var(--fast) var(--ease)}
.btn:hover{border-color:#425574;background:linear-gradient(180deg,#222d47,#182137);
  box-shadow:var(--edge), var(--sh2)}
.btn:active{transform:translateY(1px);box-shadow:var(--edge-soft), var(--sh1)}
/* ---- the gold button: struck, not painted ----
   Four stops, because polished metal has four zones — the specular line along the
   top edge, the broad lit face, the turn where the surface rolls away, and the dark
   foot. The shadow under it carries the alloy's own colour at low alpha, so the
   button appears to be throwing light onto the page rather than casting grey. */
.btn.gold{background:linear-gradient(180deg,#fbeec6 0%,#f0d48a 16%,#d9b45b 62%,#c19a43 100%);
  color:#2a1d02;border-color:#a8832f;font-weight:700;
  box-shadow:inset 0 1px 0 #fffdf4b3, inset 0 -1px 0 #7d5f2340,
    0 1px 2px #00000073, 0 10px 24px -10px #d9b45b6b}
.btn.gold:hover{background:linear-gradient(180deg,#fff7de 0%,#f7e3ae 16%,#e3c169 62%,#cca54c 100%);
  border-color:#c19a43;
  box-shadow:inset 0 1px 0 #fffefa, inset 0 -1px 0 #7d5f2340,
    0 1px 2px #00000073, 0 14px 32px -10px #d9b45b8c}
.btn.ghost{background:transparent}
.btn.danger{color:#ff9a92}
.btn.small{padding:6px 11px;font-size:var(--t2)}
.btn.block{width:100%;padding:var(--s3);margin-top:var(--s3);font-size:var(--t3)}
.btn.full{width:100%;margin-top:var(--s2)}
.price{color:var(--gold2);border-color:#3a4a2c;font-variant-numeric:tabular-nums}

/* hero — the one number the page exists for, and the two ways to use it */
.hero-left{display:flex;flex-direction:column;align-items:flex-start}
/* the strips are the full width of the column they are in; everything above them is
   text and sits at its own width */
.hero-left>.hero-avail,.hero-left>.hero-cap,.hero-left>.hero-online{align-self:stretch}
/* ============ the hero: the one number the page exists for ============
   Everything else on this page is a consequence of this figure, so it is given the
   treatment a vault door gets: a raised slab with its own light source inside it.
   Reading the background from the bottom up — the body gradient, a cold blue lift
   in the lower right that keeps the black from going dead, and a warm gold bloom
   spilling in over the top left corner, which is where every other light on this
   site comes from. The gold hairline across the top edge (::before) is the same
   line the statement draws under its letterhead and the sign-in card draws over
   its own: one ornament, used three times, so it reads as a house rule.
   ::after is the engraved corner — a quarter of a guilloché rosette, drawn by the
   browser out of three repeating gradients, clipped to the panel and held at 5%.
   It is the only purely decorative mark in the interface, it is in the corner
   furthest from every figure, and it cannot be hit, focused or read aloud. */
.hero{position:relative;overflow:hidden;
  display:flex;justify-content:space-between;align-items:center;gap:var(--s5);flex-wrap:wrap;
  margin:var(--s3) 0 var(--s2);padding:var(--s5) var(--s5);border:1px solid #233152;
  border-radius:var(--r4);
  background:
    radial-gradient(760px 300px at 10% -45%, #d9b45b21, transparent 70%),
    radial-gradient(560px 300px at 96% 130%, #2a4d8f1f, transparent 70%),
    linear-gradient(168deg,#16203a 0%,#0e1524 46%,#0a0f1b 100%);
  box-shadow:var(--edge), var(--sh3)}
.hero::before{content:"";position:absolute;left:8%;right:8%;top:0;height:1px;
  background:linear-gradient(90deg,transparent,#d9b45b,#f0d48a,#d9b45b,transparent);
  opacity:.55}
/* The engraving. It is the desk's own plate — static/guilloche.svg, cut by
   scripts/make_bullion_art.py — laid into the corner furthest from any figure, at
   six percent, half off the edge of the panel so it reads as a watermark under the
   surface rather than a badge stuck on top of it. Never on a phone: there is no
   corner to spare there, and a 340px rosette behind a balance is noise. */
.hero::after{content:"";position:absolute;right:-118px;top:-136px;width:420px;height:420px;
  pointer-events:none;opacity:.05;
  background:url(/guilloche.svg) center/contain no-repeat}
@media screen and (max-width:900px){.hero::after{display:none}}
.hero>*{position:relative}
.hero-label{color:var(--mut);font-size:var(--t2);text-transform:uppercase;
  letter-spacing:.16em;font-weight:600;font-size:var(--t1)}
/* The balance is set in Inter, never in the display serif: it is a figure that has
   to be compared with another figure, and tabular numerals are what let a reader do
   that. What it gets instead is weight, negative tracking and a hairline of warmth
   in its own shadow — the number looks struck rather than typed. */
.hero-equity{font-size:var(--t5);font-weight:750;letter-spacing:-.035em;line-height:1.05;
  margin:var(--s2) 0 var(--s1);color:#fbfcff;
  text-shadow:0 1px 0 #00000059, 0 10px 40px #d9b45b1f}
.hero-equity small{font-size:var(--t4);color:var(--gold2);font-weight:600;letter-spacing:.02em;
  opacity:.9}
.hero-meta{color:var(--gold2);font-size:var(--t2)}
.hero-meta .bad{color:var(--down)}.hero-meta .gd{color:var(--up)}
/* ============ the metric strip ============
   Three figures under the balance, five under those: what you can take out, what is
   in the bank, what margin is free — then the bar counts and the margin behind them.
   They used to be two run-on sentences, which is the one shape that makes comparing
   figures hard: the eye has to find each number inside prose, and on a phone the line
   wrapped wherever it liked.

   The construction is a grid with a 1px gap over a background: the gap IS the hairline
   between cells, so there is one border for the whole strip instead of a border per
   cell, and no cell can ever be a pixel out of line with its neighbour. Every label is
   the same height and every figure sits on the same baseline, at any width, because
   that is what lets a reader compare them at a glance rather than reading them.
   `:empty` matters — capLine() returns nothing for an account with no posted margin,
   and an empty grid would otherwise paint a bordered box with nothing in it. */
.hero-avail,.hero-cap,.hero-online{display:grid;
  grid-template-columns:repeat(auto-fit,minmax(168px,1fr));
  gap:1px;margin-top:var(--s3);padding:0;overflow:hidden;
  background:#22304a;border:1px solid #22304a;border-radius:var(--r2);
  box-shadow:var(--sh1)}
.hero-avail:empty,.hero-cap:empty,.hero-online:empty{display:none}
.mt{display:flex;flex-direction:column;gap:3px;min-width:0;padding:10px 14px 11px;
  background:linear-gradient(180deg,#111827,#0a0f1a)}
.mt-k{font-size:10.5px;text-transform:uppercase;letter-spacing:.11em;font-weight:600;
  color:var(--mut);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mt-v{font-size:17px;font-weight:700;letter-spacing:-.022em;color:var(--txt);
  white-space:nowrap;line-height:1.25}
.mt-v i{font-style:normal;font-size:11.5px;font-weight:600;color:var(--mut);
  letter-spacing:.02em;margin-left:1px}
/* the three states a figure in the strip can be in — the same three words the rest of
   the page uses, and never a fourth */
.mt.good .mt-v{color:#7ce3ae}
.mt.bad .mt-v{color:var(--down)}
.mt.warn .mt-v{color:#ffce8a}
/* the cell that carries the headline figure of the strip gets the desk's gold, so the
   first thing read in the row is the one a customer came for */
.hero-avail .mt:first-child .mt-v{color:var(--gold2)}
.hero-avail .mt:first-child{background:linear-gradient(180deg,#1a1b16,#0f1017)}
@media screen and (max-width:520px){
  .hero-avail,.hero-cap,.hero-online{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* Three figures in two columns leaves a hole, and the hole is not empty — it is the
     strip's own background showing through the 1px gap, which reads as a fourth cell
     that failed to load. An odd last cell takes the whole row instead. */
  .hero-avail .mt:last-child:nth-child(odd),
  .hero-cap .mt:last-child:nth-child(odd),
  .hero-online .mt:last-child:nth-child(odd){grid-column:1 / -1}
  .mt{padding:9px 11px 10px}
  .mt-v{font-size:15px}
  .mt-k{font-size:10px;white-space:normal}
}
/* TT-Bar margin: posted / open / available bars + used / remaining AED — the same
   strip as above, one step quieter, because it answers a question the first strip has
   already raised rather than one a customer arrives with */
.hero-cap .mt{background:linear-gradient(180deg,#0e1420,#090d16)}
.hero-cap .mt-v{font-size:15.5px}
/* Online accounts: the trading status and the 12:59 daily charge — the same strip again,
   edged in the Online colour so it is never read as the Physical margin strip above it */
.hero-online .mt{background:linear-gradient(180deg,#0d1622,#091019)}
.hero-online .mt-v{font-size:15.5px}
.hero-online{border-color:#2fb6a666;background:#2fb6a640}
/* six figures: two even rows of three (the phone keeps its two columns, three rows) — an
   auto-fit row would leave a hole in the second row that reads as a cell that failed */
@media screen and (min-width:521px){.hero-online{grid-template-columns:repeat(3,minmax(0,1fr))}}
.hero-risk{margin-top:var(--s2);font-size:var(--t2);color:#ffb04d;max-width:560px}
.hero-risk b{color:#ffce8a;font-weight:700}
.wd-box{background:#080b14;border:1px solid var(--line2);border-radius:var(--r2);
  padding:var(--s3);margin-bottom:var(--s3)}
.wd-line{font-size:var(--t3);color:#c6d1e6}
.wd-line b{font-size:var(--t3);color:var(--txt)}
.wd-line.small{font-size:var(--t2);color:var(--mut);margin-top:var(--s1)}
.hero-note{color:var(--mut);font-size:var(--t2);margin-top:var(--s2);max-width:520px}
.hero-note b{color:var(--txt);font-weight:600}
.hero-actions{display:flex;flex-direction:column;gap:var(--s3);min-width:290px}
/* ---- the balance and the two things you can do about it, side by side ----------
   The panel is a flex row that wraps, and until now it always wrapped: the figures
   are a wide block, the actions ask for 290px, and the two together never fit — so
   the buttons dropped under the text and left a third of the panel empty at every
   desktop width. Given a column of its own the panel reads the way it is meant to:
   what you have on the left, what you can do on the right, a hairline between them.

   Only for a CUSTOMER. The desk sees six controls in that column instead of two
   (deposit, withdraw, gold in/out, gold margin), and six do not belong in 318px —
   the console keeps the stacked layout it was measured on. */
@media screen and (min-width:1000px){
  body:not(.is-desk) .hero{flex-wrap:nowrap;align-items:stretch}
  body:not(.is-desk) .hero-left{flex:1 1 auto;min-width:0;align-self:center}
  body:not(.is-desk) .hero-actions{flex:0 0 318px;min-width:0;align-self:center;
    padding-left:var(--s5);border-left:1px solid #ffffff12}
}
.big-row{display:flex;gap:var(--s3)}
/* `.big` is the big Buy/Sell action, and only ever a <button>. It used to be written as a
   bare class, and a bare class lands on anything that carries it: `div.preview.big` — the
   margin/calculation panel — was turned into a shrink-to-fit inline-flex box by the phone
   rules below, so the panel laid its own sentences out as one non-wrapping row, painted
   outside the card and gave the Buy/Sell modal a horizontal scrollbar at every phone width
   (measured 16/09/2026 at 320-430px: a 376-458px panel inside a 236-306px form). Every
   rule for it is now written against the button element, so no non-button can be caught. */
button.big{position:relative;overflow:hidden;
  flex:1;padding:var(--s4) var(--s3);border-radius:var(--r3);border:0;font-size:var(--t4);
  font-weight:750;cursor:pointer;letter-spacing:-.01em;
  transition:transform var(--fast) var(--ease),filter var(--fast) var(--ease),
    box-shadow var(--fast) var(--ease)}
/* the sweep: one specular band lying across the face of the button, which is what a
   curved metal surface does with a single light. It does not move and it does not
   animate — it is a highlight, not an effect. */
button.big::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(105deg,#ffffff00 34%,#ffffff2e 48%,#ffffff00 62%)}
button.big:hover{transform:translateY(-1px);filter:brightness(1.04)}
button.big:active{transform:translateY(0)}
button.big.buy{background:linear-gradient(180deg,#fbeec6 0%,#f0d48a 14%,#d9b45b 58%,#bd9440 100%);
  color:#241a02;
  box-shadow:inset 0 1px 0 #fffdf4cc, inset 0 -2px 0 #7d5f2333,
    0 2px 4px #00000073, 0 16px 34px -12px #d9b45b73}
button.big.buy:hover{box-shadow:inset 0 1px 0 #fffefa, inset 0 -2px 0 #7d5f2333,
    0 2px 4px #00000073, 0 20px 44px -12px #d9b45b99}
/* Sell is the same object cast in a different metal: an oxidised green that belongs
   to this palette rather than the P/L green, which means one thing on this page and
   is never spent on a control. */
button.big.sell{background:linear-gradient(180deg,#24493a 0%,#1a3a2c 46%,#102419 100%);
  color:#95edc0;border:1px solid #37674e;
  box-shadow:inset 0 1px 0 #ffffff1a, 0 2px 4px #00000073, 0 14px 30px -14px #0d4c31a6}
button.big.sell:hover{box-shadow:inset 0 1px 0 #ffffff24, 0 2px 4px #00000073,
    0 18px 38px -14px #0d4c31cc}
button.big.sell::after{background:linear-gradient(105deg,#ffffff00 34%,#ffffff1a 48%,#ffffff00 62%)}
.mid-row{display:flex;gap:var(--s2)}
.mid{flex:1;text-align:center;text-decoration:none;
  background:linear-gradient(180deg,#141c2f,#0c1220);border:1px solid var(--line2);
  color:#cad5e9;border-radius:var(--r2);padding:var(--s3) var(--s2);font-size:var(--t2);
  cursor:pointer;font-weight:600;box-shadow:var(--edge-soft),var(--sh1);
  transition:border-color var(--fast) var(--ease),
  background var(--fast) var(--ease),box-shadow var(--fast) var(--ease),
  color var(--fast) var(--ease)}
.mid:hover{border-color:#425574;background:linear-gradient(180deg,#1c2640,#111828);
  box-shadow:var(--edge),var(--sh2)}
/* ---- two of the six are not like the other four -------------------------------
   The row used to be six identical grey buttons, and the owner's note was exactly
   that: "change the colour to differentiate". The four the DESK uses to move money
   and metal stay neutral — they are one group of tools. The two a CUSTOMER sees keep
   their own identity, and it is the same identity they carry in the section nav under
   this panel: `Statement` is the desk's gold (read the book), `Limit order` is steel
   blue (act on the market). Never the P/L green and red: those two colours mean the
   sign of a number on this page and nothing else. */
.mid-doc{border-color:#d9b45b5c;color:#f0d48a;
  background:linear-gradient(180deg,#d9b45b1f,#d9b45b0d)}
.mid-doc:hover{border-color:#d9b45ba6;color:#f7e3ae;
  background:linear-gradient(180deg,#d9b45b30,#d9b45b17)}
.mid-act{border-color:#5f7db473;color:#c2d8f7;
  background:linear-gradient(180deg,#6d94d821,#6d94d80f)}
.mid-act:hover{border-color:#7aa2e0b8;color:#dceaff;
  background:linear-gradient(180deg,#6d94d833,#6d94d81a)}

/* ============ the statement's four sections ============
   One strip of four, stuck to the top of the window while the document scrolls under
   it. Each section owns a colour, and the colour is the whole point: Summary and Open
   trades — the two the desk reads every morning — can never be taken for one another,
   at a glance, at any width, before reading a word.

       idle      1px border and an 8% wash of the section's own colour, plus a dot of
                 it, so the identity is visible even when the section is not the one
                 being read; the label stays a light tint, well over 4.5:1
       hover     the same colour, twice as present — a deliberate step, not a guess
       current   FILLED with the colour, dark ink on top (>= 7:1), and aria-current
                 so a screen reader is told the same thing the eye is
       focus     the site's gold ring, offset, on every one of them

   Only ever one chip is filled, so the strip is quiet — the page does not become a
   row of coloured buttons. Screen only: `no-print` takes the whole strip off the
   printed sheet and out of the PDF. */
.stmt-nav{position:sticky;top:var(--netbar-h,0px);z-index:40;display:flex;flex-wrap:wrap;gap:var(--s2);
  margin:var(--s3) 0 0;padding:var(--s3) 0;
  background:linear-gradient(180deg,var(--bg) 66%,#05070cf0 88%,#05070c00)}
/* NO backdrop-filter here, and that is a performance decision rather than a taste one.
   This strip is `position:sticky` over a document that is 25,000px long on a phone, so a
   backdrop filter on it asks the compositor to re-blur what is behind it on every scroll
   frame. Measured in WebKit on 17/09/2026: the frame that repaints the strip arrived so
   late that the section chip lagged a whole jump behind the section actually on screen
   (scripts/e2e_browser.py caught it — "the chip marked current while #sec-closed is in
   view: got Open trades"). The gradient above already hides the document sliding under
   the strip, which is all the blur was ever doing. */
.snav{display:flex;align-items:center;justify-content:center;gap:var(--s2);
  flex:0 0 auto;min-height:44px;padding:var(--s2) var(--s4);border-radius:var(--r2);
  text-decoration:none;font-size:var(--t2);font-weight:700;letter-spacing:.01em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  border:1px solid var(--sn-line);background:var(--sn-wash);color:var(--sn-ink);
  transition:background var(--fast) var(--ease),border-color var(--fast) var(--ease),
    color var(--fast) var(--ease)}
.snav::before{content:"";width:8px;height:8px;border-radius:50%;flex:none;
  background:var(--sn-dot)}
.snav:hover{border-color:var(--sn-line-h);background:var(--sn-wash-h);color:var(--sn-ink-h)}
.snav:focus-visible{outline:2px solid var(--gold2);outline-offset:3px}
.snav[aria-current="true"]{background:var(--sn-on);border-color:var(--sn-on-line);
  color:var(--sn-on-ink);box-shadow:inset 0 1px 0 #ffffff5c, var(--sh2)}
.snav[aria-current="true"]::before{background:currentColor;opacity:.5}
/* 1 — the account's own figures: the desk's gold */
.sn-summary{--sn-line:#d9b45b5c;--sn-wash:#d9b45b14;--sn-ink:#f0d48a;--sn-dot:#d9b45b;
  --sn-line-h:#d9b45ba6;--sn-wash-h:#d9b45b26;--sn-ink-h:#f7e3ae;
  --sn-on:linear-gradient(180deg,#e8cd7d,#d9b45b);--sn-on-line:#b3923e;--sn-on-ink:#2a1d02}
/* 2 — what is still on the market: steel blue */
.sn-open{--sn-line:#5f7db473;--sn-wash:#6d94d816;--sn-ink:#c2d8f7;--sn-dot:#7aa2e0;
  --sn-line-h:#7aa2e0b8;--sn-wash-h:#6d94d82b;--sn-ink-h:#dceaff;
  --sn-on:linear-gradient(180deg,#9dbdf0,#6d94d8);--sn-on-line:#587dc2;--sn-on-ink:#08142a}
/* 3 — what is finished and banked: stone, the colour of the sheet it prints on */
.sn-closed{--sn-line:#a2957a66;--sn-wash:#bfae8c14;--sn-ink:#ddd2bb;--sn-dot:#bfae8c;
  --sn-line-h:#bfae8cad;--sn-wash-h:#bfae8c26;--sn-ink-h:#f0e8d6;
  --sn-on:linear-gradient(180deg,#ded3b8,#bfae8c);--sn-on-line:#9d8d6b;--sn-on-ink:#241d0e}
/* 4 — the money in and out: lilac, the one hue nothing on this page measures in */
.sn-ledger{--sn-line:#8d81b866;--sn-wash:#a79ad814;--sn-ink:#cfc7ec;--sn-dot:#a79ad8;
  --sn-line-h:#a79ad8b0;--sn-wash-h:#a79ad828;--sn-ink-h:#e4dffa;
  --sn-on:linear-gradient(180deg,#c3b9ea,#a79ad8);--sn-on-line:#8577bd;--sn-on-ink:#140f26}
/* the sticky strip covers the top of whatever is jumped to unless the target says so */
#sec-summary,#sec-open,#sec-closed,#sec-ledger,#paper{scroll-margin-top:78px}
/* phones: two rows of two, each still a 44px target, still no sideways scroll at 320 */
@media screen and (max-width:560px){
  .stmt-nav{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;
    padding:var(--s2) 0;margin-top:var(--s3)}
  .snav{font-size:12px;padding:var(--s2) 10px;gap:7px;flex:1 1 auto}
  .snav::before{width:7px;height:7px}
  #sec-summary,#sec-open,#sec-closed,#sec-ledger,#paper{scroll-margin-top:108px}
}

/* paper statement */
/* ---- the sheet -------------------------------------------------------------
   A statement is a document, and a document on a dark screen has to look like it
   is LYING on that screen, not painted into it. The shadow is doubled — a tight
   contact edge and a wide soft fall — and a hairline of warm white runs along the
   top edge, which is the light of the room catching the paper's own thickness.
   Screen only: on paper the print block strips every one of these back off, so the
   customer's PDF is the same sheet it always was, to the millimetre. */
.paper{position:relative;background:var(--paper);color:var(--paper-txt);border-radius:18px;
  padding:26px 28px 18px;margin-top:26px;
  box-shadow:0 2px 6px #000000a6, 0 30px 80px -20px #000000d9,
    0 0 0 1px #ffffff0f, inset 0 1px 0 #ffffffe6}
.paper-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;
  border-bottom:2.5px solid var(--paper-txt);padding-bottom:10px}
/* The letterhead's own rule, on screen: a second hairline of the desk's gold laid
   just under the black one, the way an engraved letterhead is printed in two passes.
   `@media screen` because the printed sheet's rule is set in the print block and
   this must never reach it. */
@media screen{
  .paper-head{position:relative;border-bottom-width:2px}
  .paper-head::after{content:"";position:absolute;left:0;right:0;bottom:-4px;height:1px;
    background:linear-gradient(90deg,#8a6a1f,#d9b45b 45%,#8a6a1f00)}
  /* The plate, a third time: pressed into the sheet behind the letterhead. A
     statement that is worth something has always been printed on something.

     It is a BACKGROUND LAYER, not a pseudo-element, and that is a deliberate
     choice rather than a shorter way to write it. A pseudo would have needed
     `overflow:hidden` on the sheet to clip the rosette at the corner — and
     `overflow:hidden` makes the sheet a scroll container, which would have
     silently killed the sticky column headers the wide tables inside it rely on
     (`.tbl-scroll.fits table.st thead th{position:sticky}`). A background is
     clipped to the border box for free, paints under every figure without a
     single z-index, and cannot come between a reader and a number.

     The wash on top of it is the paper's own colour at 94%: that is what holds
     the engraving at the strength of a watermark instead of a pattern. Screen
     only — the PDF is the supplier's document and carries no ornament of ours. */
}
@media screen and (max-width:760px){.paper::before,.paper::after{display:none}}
/* ---- the document's masthead ------------------------------------------------
   The desk, its mark, the customer and the reference — the letterhead every statement
   carries. This is not screen decoration: app.py points headless Chrome at THIS page
   to build the PDF, so the letterhead a customer receives is this markup. */
.doc-id{display:flex;align-items:center;gap:12px;min-width:0}
.doc-logo{width:46px;height:46px;border-radius:11px;display:grid;place-items:center;flex:none;
  background:linear-gradient(150deg,#1e2a4a,#0e1524);border:1px solid var(--line2);
  color:var(--gold);font:700 17px Georgia,serif;letter-spacing:.5px}
.doc-desk-name{font-size:19px;font-weight:700;line-height:1.15}
/* the letterhead, on screen, in the desk's display face — the same voice as the
   name on the sign-in page. The printed sheet keeps the document face it has always
   had: a PDF that a bank or an auditor opens is not the place to be distinctive. */
@media screen{
  .doc-logo{border-radius:50%;font:400 20px/1 var(--display);letter-spacing:.03em;
    background:radial-gradient(circle at 34% 24%, #253254, #0b1120 76%);
    border-color:#d9b45b8c;color:var(--gold2);
    box-shadow:0 0 0 3px #d9b45b14, 0 2px 6px #00000059}
  .doc-desk-name{font-family:var(--display);font-weight:400;font-size:23px;
    letter-spacing:.004em;line-height:1.1}
  .est{font-family:var(--display);font-weight:400;letter-spacing:.2em}
}
.doc-desk-sub{color:var(--paper-mut);font-size:12px;margin-top:2px}
.doc-title{text-align:right;margin-left:auto;min-width:0}
.est{font-size:23px;font-weight:800;letter-spacing:3px;line-height:1}
.est-sub{color:var(--paper-mut);font-size:12.5px;margin-top:3px}
.paper-print{border:1px solid #ccc4ae;background:#fff;color:#4a3d10;border-radius:9px;
  padding:6px 12px;font-size:12px;font-weight:600;cursor:pointer}
.paper-actions{display:flex;gap:7px;align-items:center;flex:none}
/* A screen needs most of this document and not all of it. The margins table is printed
   only (the summary tiles already carry those figures), and so is the dollar column
   beside each AED column: paper has the width to name every currency it prints, and a
   statement must not make its reader guess which one a figure is in. */
.print-only,.usd-col{display:none!important}
.sec-n{color:var(--golddark);font-weight:800;margin-right:7px}
.st .mu{color:var(--paper-mut);font-size:11px;width:54px}
.st.margins{min-width:0}
.meta-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px 18px;margin:16px 0 6px}
.meta label{display:block;font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--paper-mut)}
.meta b{font-size:14.5px;word-break:break-word}
.sec-title{font-size:11.5px;text-transform:uppercase;letter-spacing:.9px;color:var(--paper-mut);
  margin:20px 0 8px;border-bottom:1px solid var(--paper-line);padding-bottom:6px}
/* on screen a numbered section heading is a ruled band, the way a section is opened
   in a printed report — the numeral in the desk's gold, the rule under it warm */
@media screen{
  .sec-title{letter-spacing:.14em;font-weight:700;color:#4c5062;
    border-bottom-color:#cfc5a7}
  .sec-n{color:#9a7526;font-weight:800}
}
/* 118px was the width at which "MARGIN REQUIRED" broke onto three lines and the row
   of eleven tiles turned ragged. The floor is the width the longest label needs. */
.ov-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(134px,1fr));
  border:1px solid var(--paper-line);border-radius:10px;overflow:hidden}
.ov{border-right:1px solid var(--paper-line);border-bottom:1px solid var(--paper-line);
  padding:8px 12px;background:#fff8}
.ov label{display:block;font-size:9.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--paper-mut)}
/* The card is ~95px wide inside, and a 13-character figure set in tabular numerals needs
   about 88px of it. At the old off-scale 15px it needed 102px and `word-break:break-word`
   broke it mid-number ("1,414,101." / "631"), so the value sits on the type scale and is
   never allowed to break; the `$ …` caption under it still wraps. Screen only: the printed
   sheet dresses these cells itself (10.5px, no captions) and must not move. */
@media screen{
  .ov b{font-size:var(--t2);white-space:nowrap}
  .ov b>.sub{white-space:normal;word-break:break-word}
}
/* `position:relative` is not decoration — it is what stops the page dragging sideways
   on a tablet. Every profit/loss cell carries a clipped .sr-only word ("profit"/"loss")
   for a screen reader, and .sr-only is `position:absolute`. With nothing positioned
   between it and the page, its containing block was the page itself, so a 1px span
   sitting at x=1294 inside a 1031px-wide table pushed the DOCUMENT's scroll width to
   1295px at every width from 761px to ~1290px — measured 17/09/2026: window.scrollX
   reached 400 at 768px and 271 at 1024px, with nothing visible out there to explain it.
   Making the scroller the containing block keeps those spans inside the box that was
   always meant to hold them. Phones were never affected (the tables are cards there). */
.tbl-scroll{overflow-x:auto;position:relative;border:1px solid var(--paper-line);
  border-radius:10px;background:#fff8}
table.st{width:100%;border-collapse:collapse;font-size:13px;min-width:760px}
.st th{background:#efeadd;color:#4a4f5e;text-align:left;font-size:10.3px;text-transform:uppercase;
  letter-spacing:.4px;padding:8px 10px;border-bottom:1.5px solid var(--paper-line);white-space:nowrap}
/* On screen the header band is a printed band: a shade of the paper rather than a
   grey box, with the section's own gold closing it off underneath. Print keeps the
   flat band it has always had. */
@media screen{
  .st th{background:linear-gradient(180deg,#f3eee2,#e9e2d1);letter-spacing:.07em;
    font-weight:700;border-bottom:1px solid #c9bf9f}
  .st tbody tr:hover td{background:#f1ead6}
  .st tr.day-band td:hover,.st tr.day-band:hover td{background:#e9e2cd}
}
/* supplier statement header: one grouped box row over the sub-headers (NR6 reference) */
.st thead tr.hgrp th{background:#e4ddc9;color:#3c445c;text-align:center;font-size:9.6px;
  letter-spacing:.7px;border-right:1px solid var(--paper-line);border-bottom:1px solid var(--paper-line);padding:5px 8px}
.st thead tr.hgrp th:first-child{text-align:left}
.st td{padding:9px 10px;border-bottom:1px solid #e7e2d4;white-space:nowrap;vertical-align:middle}
/* Free-text columns (ledger narration, order notes): the text must stay INSIDE its own
   cell. With white-space:nowrap + a cell max-width it used to run over the AED numbers
   of the next columns; with only wrapping it made some rows double-height, which broke
   the even statement grid on a phone (Naveed, 2026-09-11). So on screen: the narration
   wraps inside a wide column AND every ledger row keeps the same height — the long
   correction/reversal captions then use two lines instead of padding their row. */
@media screen {
  .st td.wrap{white-space:normal;overflow-wrap:break-word;min-width:210px}
  .st.ledg td.wrap{width:420px;min-width:420px;max-width:420px;height:34px;vertical-align:middle}
  .st.ledg tbody tr{height:56px}
}
.st tr:last-child td{border-bottom:none}
.st tr:nth-child(even) td{background:#f3efe4}
.st .r,th.r{text-align:right;font-variant-numeric:tabular-nums}
td.up,td.down{font-weight:700;text-align:right}
/* The screen's green and red are built for the dark panels, where they measure 9.16:1
   and 7.12:1. On the pale sheet they measure 1.80:1 and 2.31:1 — a profit and a loss
   the same unreadable smudge for anyone with low vision, in sunlight, or on paper.
   Measured, not guessed (scripts/e2e_a11y.py prints both numbers on every run). Same
   two hues, darkened until they clear 4.5:1 on the sheet AND on the banded even row
   (#f3efe4, the harder of the two). The sign in front of the figure still carries the
   meaning on its own — the colour only has to be legible. */
.paper .up,.paper td.up{color:#15724a}
.paper .down,.paper td.down{color:#a92c25}
/* A day in the ledger reads as one block: the band names it, and the day's own
   rows sit under it until the next one. Screen only — the printed sheet and the
   PDF are the supplier's layout and never carried this band.
   The band is a real <tr> with the ledger's ten cells and the ledger's own row
   height, because tests/test_suite.py::TestLongTextDoesNotOverlap holds every
   row of #ledger-body to one single height. */
.st tr.day-band td{background:#e9e2cd;border-top:1px solid #cdc4ab;border-bottom:1px solid #ddd4bd;
  color:#4a4f5e;font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
  padding-top:0;padding-bottom:0}
.st tr.day-band td.band-sum{text-align:right;color:#3c445c;font-variant-numeric:tabular-nums;
  text-transform:none;letter-spacing:0;font-size:11px}
/* The closed book's week band is the same band as the ledger's day band (see below), so
   it needs nothing of its own — and nothing of it reaches the printed sheet, because the
   band row carries `no-print`. */
/* A sticky header is only safe on a table that already fits its box: the moment the
   table is wider than the sheet, unclipping the wrapper would drag the whole PAGE
   sideways (measured on the live book: the ledger is 1206px inside a 1030px box, the
   closed book 1245px — both scroll inside their own box on purpose). So the unclip is
   earned per table: app.js measures each one and adds `fits` only when it does not
   need its scroller. Wide screen, wider sheet, and the header rides the top. */
@media screen and (min-width:1120px){
  .tbl-scroll.fits{overflow:visible}
  .tbl-scroll.fits table.st thead th{position:sticky;top:0;z-index:3}
  .tbl-scroll.fits .st.ledg thead tr.hgrp th{position:sticky;top:0;z-index:4;height:24px;
    padding-top:0;padding-bottom:0}
  .tbl-scroll.fits .st.ledg thead tr:not(.hgrp) th{top:24px}
}
/* a big screen gets a big sheet: the statement is a wide supplier document, so from
   1360px it is allowed the width it needs instead of scrolling sideways */
@media screen and (min-width:1360px){
  .appbar,.wrap,.admin-notice,.netbar,.skel{max-width:1400px}
}
/* ---- the closed book fits the wide sheet (10/10/2026) ----------------------------
   Eighteen columns at 10px of padding a side measured 1,318px — eighteen pixels wider
   than the 1,300px sheet a 1440 window gets, so the last column, Net P/L, was clipped
   at the edge of its scroller on every desk screen (it showed as "−1,765.11" with the
   dollar line cut, and the desk's Correct chip on a line under it). Three pixels less
   a side on this one table is sixty pixels back: 1,228px, and it fits at 1366 too. */
@media screen and (min-width:761px){
  table.st.cards-closed th,table.st.cards-closed td{padding-left:7px;padding-right:7px}
}
/* ---- a table that has to scroll says so ---------------------------------------
   Between 761px and the wide sheet, the closed book and the ledger are wider than
   their box and scroll inside it — correctly, but silently: nothing told the eye the
   columns went on. app.js wraps every scroller in `.tbl-wrap` and marks the ones
   that overflow; the wrap (not the scroller, whose children would scroll away with
   the rows) draws the fade and the hint at the right edge, and drops both when the
   last column has been reached. Never on the printed sheet. */
.tbl-wrap{position:relative}
.tbl-wrap.scrolls::after{content:"";position:absolute;top:1px;right:1px;bottom:1px;width:56px;
  border-radius:0 10px 10px 0;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--paper) 92%);
  opacity:1;transition:opacity var(--slow) var(--ease)}
.tbl-wrap.scrolls::before{content:"Scroll →";position:absolute;right:10px;top:50%;z-index:2;
  transform:translateY(-50%);pointer-events:none;
  padding:5px 9px;border-radius:999px;font-size:11px;font-weight:700;letter-spacing:.04em;
  color:var(--gold-shade);background:linear-gradient(180deg,var(--gold-hi),var(--gold-mid));
  box-shadow:0 2px 8px #00000055;opacity:1;transition:opacity var(--slow) var(--ease)}
.tbl-wrap.scrolls.at-end::after,.tbl-wrap.scrolls.at-end::before{opacity:0}
/* ---- the long books open a page at a time (10/10/2026) -------------------------
   A closed book of forty trades and a ledger of a hundred entries were all on the
   page at once, and the results panel sat seven thousand pixels down. The newest
   rows stay; the rest wait behind one plain button that says how many there are.
   The rows are in the DOM the whole time (hidden, not absent), so a search, a
   screen reader's table summary and the PDF renderer all see the whole book — and
   the printed sheet, which never has a button, shows every row. */
.more-hidden{display:none}
.show-more{display:block;width:100%;margin:10px 0 2px;padding:12px 14px;min-height:44px;
  border:1px dashed var(--paper-line);border-radius:10px;background:var(--doc-chip,#fff8);
  color:var(--paper-txt);font:inherit;font-size:13px;font-weight:700;letter-spacing:.01em;
  cursor:pointer;transition:background var(--fast) var(--ease),border-color var(--fast) var(--ease)}
.show-more:hover{background:var(--doc-hover,#f1ead6);border-color:var(--gold-deep)}
.show-more:focus-visible{outline:2px solid var(--gold2);outline-offset:2px}
.show-more small{display:block;font-weight:500;color:var(--paper-mut);font-size:11.5px;margin-top:2px}
.total-row{display:flex;justify-content:flex-end;gap:22px;padding:10px 6px 2px;font-size:13px;color:var(--paper-mut)}
.total-row b{color:var(--paper-txt);font-size:15px}
.mini{display:inline-flex;gap:5px;align-items:center;justify-content:flex-end}
.mini button{border:1px solid #c9c2ae;background:#fff;color:#4a3d10;border-radius:8px;padding:3px 9px;
  font-size:11.5px;cursor:pointer;font-weight:600}
.mini button:hover{background:#f5eed6}
.mini button.d{color:#b3403a;border-color:#e2b9b5}
.mini button.armed{background:#f7ecd0;border-color:#d9b45b;color:#6a4e0a}
.mini button.hit{background:#d9f2e2;border-color:#7ec9a0;color:#14673c}
.paper-foot{margin-top:22px;padding-top:10px;border-top:1px solid var(--paper-line);
  font-size:11px;color:var(--paper-mut);display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap}
.hint{color:var(--mut);font-size:12.5px;margin-top:16px;text-align:center;max-width:760px;margin-left:auto;margin-right:auto}

/* results panel */
/* ---- a panel: the same slab as the hero, one step quieter ---- */
.panel{position:relative;border:1px solid #1e293e;border-radius:20px;margin-top:24px;
  background:
    radial-gradient(620px 240px at 88% -30%, #d9b45b0f, transparent 68%),
    linear-gradient(170deg,#121a2c,#0a0e19 70%);
  padding:22px 24px;box-shadow:var(--edge-soft), var(--sh2)}
.panel::before{content:"";position:absolute;left:10%;right:10%;top:0;height:1px;
  background:linear-gradient(90deg,transparent,#d9b45b52,transparent)}
/* ---- the section masthead ----------------------------------------------
   Every panel on this site — the results, the security card, each card in the desk
   console — opens the same way: a short rule of the desk's gold, then the name of the
   section in the display face, then one line of plain sentence under it saying what it
   is. Three elements, one rhythm, and the eye learns it once.

   The rule is drawn on the heading's own ::before rather than added to the markup,
   because it is an ornament and not a thing a screen reader should meet: it carries no
   text, so it is announced as nothing at all. */
.pro-head h2,.sec-card h2,.adm-card h2{font-family:var(--display);font-weight:400;
  font-size:25px;letter-spacing:.006em;line-height:1.16;color:#fbf6ea}
.pro-head h2::before,.sec-card h2::before,.adm-card h2::before{
  content:"";display:block;width:32px;height:2px;border-radius:2px;margin:0 0 11px;
  background:linear-gradient(90deg,var(--gold),#d9b45b00)}
.pro-head p,.adm-card h2+p{color:var(--mut);font-size:var(--t2);margin-top:5px;
  letter-spacing:.008em}
.pro-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:16px}
/* On a phone the section's name and the button beside it are fighting for one row:
   the heading wraps to two lines under a button that has taken half the width. They
   stack — name, sentence, then the action across the full width, where a thumb can
   reach it. */
@media screen and (max-width:620px){
  .pro-head{flex-direction:column;align-items:stretch;gap:var(--s3)}
  .pro-head>.btn,.pro-head>button{width:100%;min-height:46px;font-size:var(--t3)}
  .pro-head h2,.sec-card h2,.adm-card h2{font-size:22px}
}
.pro-head{margin-bottom:20px}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(136px,1fr));gap:10px}
.stat{background:linear-gradient(180deg,#121a2b,#0c1220);border:1px solid #1e293e;
  border-radius:12px;padding:11px 13px;box-shadow:var(--edge-soft)}
.stat .k{color:var(--mut);font-size:10.5px;text-transform:uppercase;letter-spacing:.09em;
  font-weight:600}
.stat .v{font-size:17px;font-weight:700;margin-top:3px;word-break:break-word;
  letter-spacing:-.02em}
.chart-card{margin-top:16px;background:linear-gradient(180deg,#111827,#0b0f1b);
  border:1px solid #1e293e;border-radius:14px;padding:14px 16px 8px;
  box-shadow:var(--edge-soft)}
.chart-head{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:6px}
.chart-head b{font-size:13.5px}
.chart-legend{display:flex;gap:14px;color:var(--mut);font-size:11.5px;flex-wrap:wrap;align-items:center}
.lg{display:inline-block;width:14px;height:3px;border-radius:2px;margin-right:5px;vertical-align:middle}
.lg.eq{background:#d9b45b}.lg.bal{background:#8391aa}
#eq-svg{width:100%;height:auto;display:block}
#eq-svg text{fill:#8391aa;font-size:11px;letter-spacing:.02em}
/* the chart's own figures (axis labels, the last-value tag) line up like every other
   figure on the page — digits only, so the curve itself is untouched. Screen only: the
   chart is part of the printed sheet and that sheet is left exactly as it was. */
@media screen{
  #eq-svg text{font-variant-numeric:tabular-nums}
}
#eq-svg .grid{stroke:#1a2438}
#eq-svg .line-bal{stroke:#8391aa;stroke-width:1.6;stroke-dasharray:5 5;fill:none}
#eq-svg .line-eq{stroke:#e5c271;stroke-width:2.4;fill:none;stroke-linejoin:round;
  stroke-linecap:round}
#eq-svg .area-eq{fill:url(#eqgrad)}
.journal-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin:20px 0 10px;flex-wrap:wrap}
.journal-head h3{font-size:13.5px}
.muted{color:var(--mut);font-size:12px}
.journal{border:1px solid #1e293e;border-radius:12px;overflow:hidden;max-height:360px;
  overflow-y:auto;background:#080c15;box-shadow:inset 0 1px 0 #ffffff08}
.j-item{display:flex;align-items:center;gap:11px;padding:9px 13px;border-bottom:1px solid #141d30;
  font-size:13px;transition:background var(--fast) var(--ease)}
.j-item:last-child{border-bottom:none}
.j-item:nth-child(odd){background:#0b101c}
.j-item:hover{background:#121b2e}
.j-ic{width:22px;height:22px;border-radius:7px;display:grid;place-items:center;font-size:11px;flex:none;
  background:#1d2846;border:1px solid var(--line2);color:var(--mut)}
.j-ts{color:var(--mut);font-size:11.5px;flex:none;width:76px}
.j-tx{flex:1;color:#dce4f2}
.j-del{background:0 0;border:0;color:#5d6b85;cursor:pointer;font-size:13px;padding:3px 8px;border-radius:6px}
.j-del:hover{color:#ff7a70;background:#2a1520}
.t-deposit,.t-sell,.t-live{color:#37d188}.t-withdraw,.t-down{color:#ff7a70}
.t-buy{color:#f0d48a}.t-market{color:#8fb7ff}.t-alert{color:#f5c86e}.t-settings{color:#a9b7d0}
.t-delete{color:#8e9ab1}

/* dual currency */
.sub{display:block;font-size:10.5px;font-weight:500;opacity:.75;margin-top:1px}
.up{color:var(--up)}.down{color:var(--down)}.goldc{color:var(--gold)}

/* modal bits — one title, one gold button that does the thing, one grey way out */
.m-help{color:var(--mut);font-size:var(--t2);margin:calc(-1 * var(--s1)) 0 var(--s3)}
.overlay{position:fixed;inset:0;background:#03050ae8;backdrop-filter:blur(6px) saturate(.9);
  -webkit-backdrop-filter:blur(6px) saturate(.9);display:none;
  align-items:flex-start;justify-content:center;z-index:50;overflow-y:auto;padding:var(--s6) var(--s4)}
.overlay.open{display:flex;animation:nrFade var(--slow) var(--ease)}
.modal{position:relative;
  background:
    radial-gradient(420px 180px at 50% -20%, #d9b45b17, transparent 70%),
    linear-gradient(175deg,#192338,#0b111d 72%);
  border:1px solid #33456b;
  border-radius:var(--r4);width:100%;max-width:400px;padding:var(--s5);
  box-shadow:var(--edge), var(--sh4);
  animation:nrRise var(--slow) var(--ease)}
/* the same gold hairline the hero and the sign-in card carry, so a dialog reads as
   part of the same object and not as a browser window that opened on top of one */
.modal::before{content:"";position:absolute;left:14%;right:14%;top:0;height:1px;
  background:linear-gradient(90deg,transparent,#d9b45b8c,transparent)}
.modal>*{position:relative}
.modal-head h2{font-family:var(--display);font-weight:400;font-size:22px;letter-spacing:.006em}
.modal.wide{max-width:460px}
.modal-head{display:flex;justify-content:space-between;align-items:flex-start;
  gap:var(--s3);margin-bottom:var(--s2)}
.modal-head h2{font-size:var(--t4);line-height:1.3;padding-top:var(--s2)}
.x{cursor:pointer;border:0;background:0 0;color:var(--mut);font-size:var(--t3);
  border-radius:var(--r1);min-width:44px;min-height:44px;
  transition:background var(--fast) var(--ease),color var(--fast) var(--ease)}
.x:hover{color:#fff;background:#1e2a4a}
.modal-head h2{line-height:1.25}
.frow{margin-bottom:var(--s4)}
.frow label{display:block;font-size:var(--t2);color:#bcc8de;margin-bottom:var(--s2)}
.frow em{color:var(--gold2);font-style:normal}
/* a field is a well cut into the surface: darker than what surrounds it, with the
   shadow of its own top edge falling into it. The focus ring is the desk's gold. */
input[type=number],input[type=text],input[type=password]{width:100%;
  background:linear-gradient(180deg,#05080f,#090d18);
  border:1px solid var(--line2);color:var(--txt);border-radius:var(--r2);padding:var(--s3);
  font-size:var(--t3);outline:0;min-height:52px;
  box-shadow:inset 0 2px 5px #00000073;
  transition:border-color var(--fast) var(--ease),box-shadow var(--fast) var(--ease),
    background var(--fast) var(--ease)}
/* the primary action and the way out of every dialog, side by side */
.modal-actions{display:flex;gap:var(--s2);align-items:stretch;margin-top:var(--s3)}
.modal-actions .btn{min-height:48px;font-size:var(--t3)}
.modal-actions .btn:first-child{flex:1 1 auto}
.modal-actions .btn.ghost{flex:0 0 auto;min-width:96px}
.modal-actions.end{justify-content:flex-end}
.wd-actions{display:flex;gap:var(--s2);flex-wrap:wrap;margin:calc(-1 * var(--s1)) 0 var(--s3)}
input:disabled{opacity:.45;cursor:not-allowed}
input:focus{border-color:var(--gold);
  box-shadow:inset 0 2px 5px #00000073, 0 0 0 3px #d9b45b2b, 0 0 22px -6px #d9b45b52}
.wipe-zone{margin-top:18px;padding-top:14px;border-top:1px dashed var(--line2)}
.wipe-zone .btn{margin-top:0;margin-bottom:8px}
.wipe-zone p{font-size:11.5px;line-height:1.5}
.stepper{display:flex;gap:8px;align-items:center}
.stepper input{text-align:center;font-size:22px;font-weight:700;padding:8px}
.step{flex:none;width:52px;height:52px;border-radius:13px;border:1px solid var(--line2);
  background:#1d2846;color:var(--gold2);font-size:24px;font-weight:700;cursor:pointer}
.step:hover{background:#233054}
.adv{background:var(--lift1);border:1px solid var(--line);border-radius:11px;
  padding:8px 13px;margin-bottom:14px;box-shadow:inset 0 1px 3px #00000059}
.adv summary{cursor:pointer;color:var(--mut);font-size:12.5px;font-weight:600}
.adv .frow{margin-top:10px;margin-bottom:10px}
.chip-row{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px}
.chip-row button{border:1px solid var(--line2);background:#121b2f;color:#ced9ee;border-radius:999px;
  padding:6px 12px;font-size:12.5px;cursor:pointer;font-weight:600}
.chip-row button:hover{border-color:var(--gold);color:var(--gold2)}
.preview{background:var(--lift1);border:1px dashed var(--line2);border-radius:11px;
  padding:11px 13px;font-size:13.5px;color:var(--mut);margin:2px 0 4px;
  box-shadow:inset 0 1px 3px #00000059}
/* The panel is a BLOCK that fills the dialog, at every width — never a shrink-to-fit box
   and never a row of columns. Its sentences (the locked-margin line, "Free margin after",
   the TT-bar line) are ordinary text and wrap as text; `break-word` lets prose break but
   leaves a figure's own width alone, so no number is split mid-digit. The modal is a
   narrow column on a phone (236px of content at 320px), so the panel has to be the width
   of that column and let every line flow inside it. */
.preview.big{font-size:15px;display:block;width:100%;max-width:100%;box-sizing:border-box;
  min-width:0;overflow-wrap:break-word}
.preview .main{color:var(--gold2);font-weight:700;font-size:19px;display:block;font-variant-numeric:tabular-nums}
.preview .warn{color:#ffb04d;font-weight:600}
.preview .no{color:var(--down);font-weight:700}
/* TT Bar capital projection (deposit modal) — plain desk numbers, no decoration */
.preview.cap{padding:0;border-style:solid;border-color:#233152;background:#080b14}
.preview.cap:empty{display:none}
.cap-head{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:#7f8ea8;
  padding:9px 13px;border-bottom:1px solid #233152}
.cap-row{display:flex;justify-content:space-between;gap:12px;padding:7px 13px;font-size:13.5px;
  border-bottom:1px solid #18233a}
.cap-row:last-child{border-bottom:0}
.cap-row span:last-child{color:var(--txt);font-weight:600;font-variant-numeric:tabular-nums}
.cap-row.hero span:last-child{color:var(--gold2);font-weight:700}
.cap-row.ok span:last-child{color:#7ce3ae}
.cap-row.no span:last-child{color:var(--down)}
/* correction / reversal — desk only */
.fix-btns{display:inline-flex;gap:5px;vertical-align:middle}
.fix-btns button{font-size:11.5px;padding:3px 8px;border-radius:7px;cursor:pointer;
  background:#1a2440;color:#c6d1e6;border:1px solid #26324c;font-weight:600}
.fix-btns button:hover{background:#233152}
.fix-btns button.rev{color:#ffb04d;border-color:#4a3a1e}
.voided-row td{opacity:.62}
.tag-voided{display:inline-block;font-size:10.5px;font-weight:700;letter-spacing:.05em;
  padding:2px 6px;border-radius:6px;background:#3a2020;color:#ff9a92;border:1px solid #5a2b2b}
.check{display:flex;gap:9px;align-items:flex-start;font-size:13.5px;color:#c6d1e6;margin:4px 0 10px;cursor:pointer}
.check input{margin-top:3px}
.btn.buy-big{background:linear-gradient(180deg,#e9ce80,#d9b45b 55%,#c79e44);color:#241a02;border:0;
  font-size:17px;font-weight:800;border-radius:14px;padding:15px}
.btn.sell-big{background:linear-gradient(180deg,#1d3a2c,#13291e);color:#7ce3ae;border:1px solid #2c5a42;
  font-size:17px;font-weight:800;border-radius:14px;padding:15px}
.toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%) translateY(90px);
  background:linear-gradient(180deg,#1f2b45,#141c30);border:1px solid #33456b;color:var(--txt);
  padding:13px 22px;border-radius:14px;
  font-size:14.5px;box-shadow:var(--edge), var(--sh4);opacity:0;transition:.25s;z-index:99;
  max-width:92vw;text-align:center}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
.toast.err{border-color:#ff7a7099}

@media screen and (max-width:760px){
  .wrap{padding:8px 12px 60px}
  .hero{padding:20px 18px}
  .hero-equity{font-size:40px}
  .hero-actions{min-width:100%}
  button.big{padding:15px;font-size:16px}
  .paper{padding:16px 12px;margin-top:18px}
  .ov-grid{grid-template-columns:repeat(auto-fit,minmax(105px,1fr))}
  .brand h1{font-size:16px}
}

/* The desk console table is twelve columns wide; on a phone it dragged the whole page
   sideways (measured 425px of overflow at 390px). It scrolls inside its own box instead,
   so the page keeps its shape and the table stays fully reachable by swiping. */
.adm-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; }
@media screen and (max-width:760px){
  .adm-table th, .adm-table td { padding:7px 6px; font-size:12px; }
  .adm-actions button { padding:4px 8px; font-size:11px; }
}

@media print{
  /* the folded books unfold on paper, and the screen's own furniture around a table
     (the fold button, the scroll fade and its hint) has no sheet to be on */
  .more-hidden{display:table-row}
  .show-more{display:none}
  .tbl-wrap.scrolls::before,.tbl-wrap.scrolls::after{content:none;display:none}
  /* A4 with a document margin (13mm sides, 14mm head, 15mm foot). app.py asks Chrome
     for the same paper (`preferCSSPageSize`), so what Ctrl+P gives the desk and what
     the renderer gives the customer's PDF are the same sheet with the same margins. */
  @page{size:A4;margin:14mm 13mm 15mm}
  /* Nineteen columns do not fit A4 portrait; twelve do. Instead of shrinking the register's
     type to illegibility it prints sideways on a fold-out sheet — a named page whose size
     differs from the document's, which Chrome paginates as its own sheet and returns to
     portrait after (measured: 595x842pt, 842x595pt, 595x842pt). */
  @page wide{size:A4 landscape;margin:12mm 13mm 14mm}
  .sheet-wide{page:wide}

  /* ---- THE PRINTED SHEET IS PINNED TO THE FACE IT WAS LAID OUT IN ----
     Inter arrived for the screen on 17/09/2026. The PDF a customer receives — and the
     Excel beside it — are laid out to the millimetre against the document face this
     sheet has always used, and a wider face silently re-flows a nineteen-column
     landscape register. So print keeps that stack, and the body's own optical tracking
     (a screen refinement) is reset to normal here as well: every column width, page
     break and sheet count in the PDF is exactly what it was before the design pass. */
  html,body{background:#fff;font-size:10.5px;line-height:1.4;
    font-family:-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
    letter-spacing:normal;font-feature-settings:normal}
  /* The room's light, texture and grain are a screen object and nothing else: on
     paper they would be a grey wash over every figure, and in the customer's PDF
     they would be a grey wash that cost a megabyte. `body>*` below cannot reach a
     pseudo-element, so it is named here. */
  body::before{content:none!important;display:none!important}
  body>*{display:none!important}
  body>.wrap{display:block!important}
  .no-print{display:none!important}
  .print-only{display:block!important}
  .usd-col{display:table-cell!important}
  .wrap{max-width:100%;padding:0}

  /* the paper loses its screen clothes: no shadow, no radius, no margin of its own —
     the @page box above is the only margin this document has */
  .paper{box-shadow:none;border-radius:0;padding:0;border:0;margin:0;color:var(--paper-txt)}

  /* ---- masthead, at document size, in ink and gold only ---- */
  .paper-head{border-bottom:2.5px solid var(--golddark);padding-bottom:8px}
  .doc-logo{width:40px;height:40px;border-radius:8px;font-size:15px;background:#fff;
    border:1.5px solid var(--golddark);color:var(--golddark)}
  .doc-desk-name{font-size:17px}
  .doc-desk-sub{font-size:9.5px}
  /* the two halves of the letterhead share the sheet: the title block keeps a floor and
     wraps its caption inside it, instead of overflowing off the paper — a flex item with
     `min-width:0` shrinks past its content and the text leaves the page edge (seen at
     `ESTIMATE`, clipped, in the first render) */
  .doc-title{margin-left:auto;max-width:38%}
  /* The screen can track ESTIMATE out to 3px because a screen is not a document you
     search. On paper it is: at 2.5px the glyphs are far enough apart that a PDF text
     extractor reads them as separate words ("E S T I M AT E"), so the one word that
     says this is not a final statement is the one word a search cannot find. The
     tracking is kept — the letterhead still reads as a letterhead — at the widest
     spacing that still extracts as a word. */
  .est{font-size:19px;color:var(--golddark);letter-spacing:.5px}
  .est-sub{font-size:9.5px;overflow-wrap:break-word}
  /* Seven columns because the reference number takes two: `CODE-STMT-20260915144621` is
     25 characters of machine text, and in a sixth of the sheet it broke across two lines
     — a document number that a reader cannot copy in one piece. The value cell also
     prints a step smaller, the way a document prints a serial it does not want read
     aloud, and never breaks inside itself. */
  .meta-grid{grid-template-columns:repeat(7,1fr);margin:10px 0 2px;gap:5px 12px}
  .meta.wide{grid-column:span 2}
  .meta.wide b{font-size:10px;white-space:nowrap}
  .meta label{font-size:7.5px}
  .meta b{font-size:11px}

  /* A caption under a figure is how this document says which currency it is in: it
     survives on paper everywhere except the tables, which carry a column each instead. */
  .sub{display:block;font-size:8.5px;font-weight:500;opacity:.85}
  .st .sub{display:none}

  /* ---- numbered section headings ----
     A heading never sits alone at the foot of a page: it travels with what it names. */
  .sec-title{margin:13px 0 5px;font-size:10px;padding-bottom:3px;color:var(--golddark);
    border-bottom:1px solid var(--golddark);break-after:avoid;page-break-after:avoid}
  .sec-n{margin-right:6px}

  /* ---- the summary grid: a tile is never split across pages ----
     Six tiles across, the way the screen lays them out on a desk — the phone's two-column
     stack is a screen rule and now says `screen` in its media query. It did not, and
     because the printed page area (696px) is under its 760px breakpoint the statement
     quietly printed the phone layout: a two-column summary and phone paddings. */
  .ov-grid{grid-template-columns:repeat(6,1fr);break-inside:avoid;page-break-inside:avoid}
  .ov{padding:5px 7px;break-inside:avoid;page-break-inside:avoid}
  .ov label{font-size:7px}
  .ov b{font-size:10.5px;white-space:normal;word-break:break-word}

  /* ---- tables ----
     No row is ever cut in half, and a table that runs past the foot of a page reprints
     its column headers at the top of the next one (Chrome repeats `thead` itself). */
  .tbl-scroll{overflow:visible;border:1px solid var(--paper-line);border-radius:0;background:#fff}
  /* auto layout on paper: the sheet is a fixed 184mm wide, and a column gets the width
     its content asks for instead of an equal share nobody fits in. With fixed layout a
     `Total Value (AED)` header in a 38mm column ran over its neighbour — measured on the
     closed book, which carries eighteen columns. */
  table.st{min-width:0;width:100%;font-size:10px;table-layout:auto}
  .st thead{display:table-header-group}
  tr{break-inside:avoid;page-break-inside:avoid}
  /* the numbered money headers wrap onto two lines rather than overlap the column beside
     them: `.st .r{white-space:nowrap}` (the screen rule) outranks `.st th` by specificity,
     so the print sheet has to name the cells it wants wrapped */
  /* a heading breaks at its spaces and never inside a word (`CREDI/T`), which also sets
     the column's floor to its longest word — the data cells below hold the real width */
  .st th,.st th.r,.st th.c{font-size:8px;padding:4px 5px;white-space:normal;
    word-break:normal;overflow-wrap:normal;background:#efeadd;color:#4a4f5e}
  .st td{padding:5px 5px;white-space:normal;word-break:break-word;overflow-wrap:anywhere}
  /* A figure and a date are one token: `overflow-wrap:anywhere` gives such a cell a floor
     of a single character, and auto layout then hands the column 24px — the date printed
     as `2026-` / `09-` / `04` and the reference number as one letter per line. The two
     trading tables keep their cells whole; the register has the landscape sheet to fit in,
     and the open book prints a shade smaller to fit the portrait one. */
  .cards-active th,.cards-closed th{font-size:7.5px}
  .cards-active td,.cards-closed td{white-space:nowrap;word-break:keep-all}
  .st .r{white-space:nowrap;word-break:keep-all}
  /* the ledger's narration column keeps a third of the sheet and grows downward —
     the fixed 420px box and the even 56px row height are screen furniture (they exist
     to stop a long caption running over the AED figures beside it, which cannot happen
     on paper, where the row simply gets taller and the table breaks between rows) */
  .st.ledg td{white-space:nowrap;word-break:keep-all}
  .st.ledg td.wrap{white-space:normal;overflow-wrap:break-word;word-break:break-word;
    width:34%;min-width:0;max-width:34%;height:auto}
  .st.ledg tbody tr{height:auto}
  /* The v2 screen rules for the ledger (`.st.ledg{min-width:1020px}` and a 12.5px cell
     size) are two classes deep and outrank `table.st`, so paper has to say it again: left
     as they were, the supplier-format cash statement kept its 1020px scroll width and lost
     everything past its ninth column off the right edge of the sheet. */
  table.st.ledg{min-width:0}
  table.st.ledg td{font-size:10px}
  .st.margins .mu{font-size:9.5px;width:auto}
  /* `.fits` pins the header row with `position:sticky` so it survives a scroll; the
     paginator repeats `thead` on its own, and a sticky row gets painted over the rows
     beneath it page after page */
  .tbl-scroll.fits table.st thead th{position:static}
  .total-row{font-size:10px;padding:6px 2px 0;break-inside:avoid}
  .total-row b{font-size:11px}
  /* the margins section and the closing line are one block: the line alone at the foot of
     a page opened a last sheet carrying nothing but itself */
  .closing{break-inside:avoid;page-break-inside:avoid}
  .paper-foot{margin-top:10px;padding-top:6px;font-size:8.5px;break-inside:avoid}
}

/* ---- v2 additions ---- */
.acct-chip{font-size:12px;color:var(--gold2);border:1px solid #d9b45b52;border-radius:20px;
  padding:5px 12px;font-variant-numeric:tabular-nums;font-weight:600;letter-spacing:.05em;
  background:#d9b45b0f;box-shadow:inset 0 1px 0 #ffffff0a}
/* The account's trading context, the first words under its name: Physical in the desk's
   gold, Online in its own teal (not the customer badge's blue) — the one place the two
   kinds of account are told apart at a glance, without widening the header row. */
.hdr-type{font-weight:700}
.hdr-type.t-physical{color:var(--gold2)}
.hdr-type.t-online{color:#6fe0cf}
/* Physical-only desk controls (gold in/out, gold margin) are not offered on an Online
   account — the server refuses them there */
body.is-online .phys-only{display:none!important}
#daily-wrap[hidden]{display:none}
.filter-bar{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;margin:2px 0 10px;
  padding:11px 12px;border:1px dashed #cdc3a4;border-radius:10px;background:#fffdf6}
.filter-bar label{font-size:12px;color:var(--paper-mut);display:flex;flex-direction:column;gap:4px}
.filter-bar input,.filter-bar select{background:#fff;border:1px solid #c9c2ae;color:#4a3d10;border-radius:8px;padding:7px 9px;font-size:13px}
.st.ledg{min-width:1020px}
.st.ledg td{font-size:12.5px}
.st.ledg td b{font-variant-numeric:tabular-nums}
tr.muted-row td{color:#8b8571}
tr.muted-row td b{color:#6b654f}
tr.muted-row{background:#f7f3e6!important}
.btn.d{background:#3a1520;color:#ff9a92;border-color:#5c2430}
.adm-wrap .adm-card{background:#0e1524;border:1px solid #1e293e;border-radius:12px;padding:18px}
.modal select{width:100%;box-sizing:border-box;background:#070910;border:1px solid #28344d;color:#f0f3fa;border-radius:9px;padding:13px 14px;font-size:16px}
tr.opening-row td{font-weight:600;background:#f7ecd0!important}

/* ---- statement paper actions: readable on the light paper + phones ---- */
.paper .btn.ghost,
.paper-actions .btn.ghost{background:#ffffff;color:#4a3d10;border:1px solid #b9b09a;box-shadow:0 1px 0 #ffffff55}
.paper .btn.ghost:hover{background:#f5eed6;border-color:#8f825e}
.paper .btn.gold{box-shadow:0 1px 0 #ffffff55}
.paper-head{flex-wrap:wrap;align-items:flex-start}
.paper-actions{flex-wrap:wrap}
@media screen and (max-width:760px){
  .paper-actions{width:100%;order:3;margin-top:12px}
  .paper-actions .btn{flex:1 1 auto;min-width:0;white-space:normal;padding:9px 8px;font-size:12.5px}
  .paper-actions .btn:empty{display:none}
}

/* ---- phone refinements (professional mobile pass) ---- */
@media screen and (max-width:760px){
  .appbar{padding:10px 12px 2px;gap:8px}
  .brand{gap:9px}
  .logo{width:34px;height:34px;font-size:13px;border-radius:10px}
  .brand h1{font-size:15.5px}
  .brand span{font-size:11px}
  .top-right{gap:5px;flex-wrap:wrap;justify-content:flex-end}
  .top-right .btn{font-size:11.5px;padding:5px 9px}
  .acct-chip{font-size:10.5px;padding:3px 8px}
  .hero{padding:18px 16px;gap:14px;margin-top:8px}
  .hero-equity{font-size:38px}
  .hero-meta,.hero-avail{font-size:12.5px}
  .hero-note{font-size:12px}
  button.big{font-size:15px;padding:12px 22px}
  .big-row{width:100%;gap:8px}
  .mid-row{width:100%;gap:6px}
  .mid{font-size:12px;padding:9px 10px}
  .paper{padding:14px 12px 12px;border-radius:14px}
  .paper-actions{width:100%;order:3;margin-top:10px}
  .paper-actions .btn{flex:1 1 auto;min-width:0;white-space:normal;padding:9px 8px;font-size:12px;text-align:center}
  .meta-grid{grid-template-columns:repeat(2,1fr);gap:6px 10px}
  .meta{border-bottom:1px solid var(--paper-line);padding-bottom:4px}
  .ov-grid{grid-template-columns:repeat(2,1fr)}
  .ov{padding:8px 8px;min-height:0}
  .ov b{font-size:var(--t2)}
  .stat{min-width:44%;padding:10px}
  .chart-card{padding:12px 8px}
  .modal{padding:20px 14px;width:calc(100% - 22px);max-height:92dvh;overflow-y:auto}
  .modal.wide{width:calc(100% - 22px)}
  .modal-head h2{font-size:17px}
  .m-help{font-size:12.5px}
  .frow label{font-size:13px}
  .stepper input{font-size:15px}
  input[type=number],input[type=text],input[type=date],input[type=password],.modal select{font-size:16px}
  .chip-row{flex-wrap:wrap}
  .js .j-item{font-size:12.5px}
  .stats{display:flex;flex-wrap:wrap;gap:8px}
  .filter-bar{gap:8px;padding:8px}
  .filter-bar label{font-size:11.5px}
  .filter-bar input,.filter-bar select{padding:8px 8px;font-size:14px;min-width:0}
  .wrap{padding:8px 10px 60px}
}

/* admin view-as-customer banner */
.admin-notice{max-width:1120px;margin:8px auto 0;padding:12px 16px;border-radius:14px;
  background:linear-gradient(180deg,#d9b45b26,#d9b45b0f);border:1px solid #d9b45b6b;
  color:var(--txt);font-size:13.5px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  box-shadow:var(--edge-soft), var(--sh1)}
.admin-notice b{color:var(--gold2)}
/* `hidden` must win over display:flex (an empty gold bar was showing on the customer page) */
.admin-notice[hidden]{display:none}
.notice-btn{margin-left:auto;background:#d9b45b;color:#2a1d02;text-decoration:none;font-weight:700;
  padding:7px 13px;border-radius:9px;font-size:12.5px;white-space:nowrap}
.notice-btn:hover{background:#e8cd7d}
@media screen and (max-width:760px){.admin-notice{font-size:12px;padding:9px 12px}.notice-btn{width:100%;text-align:center;margin-left:0}}

/* ---------------- two-factor (TOTP) ----------------
   The Security card on the account page and the desk's 2FA controls. All of it is
   plain markup: no external library, no CDN, and every button is a 44px touch target
   so the panel works on a phone with one hand. */
.sec-card{margin-top:18px}
.sec-card [hidden]{display:none !important}
.sec-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:4px 0 14px}
.sec-badge{display:inline-flex;align-items:center;gap:7px;padding:7px 13px;border-radius:20px;
  font-size:13px;font-weight:700;letter-spacing:.015em;
  background:linear-gradient(180deg,#ff5f5f24,#ff5f5f12);color:#ff9a92;
  border:1px solid #ff5f5f4d;box-shadow:var(--edge-soft)}
.sec-badge.on{background:#3ddc971f;color:#7ce3ae;border-color:#3ddc9744}
.sec-badge .dot{width:8px;height:8px;border-radius:50%;background:currentColor}
.sec-note{color:#98a2b5;font-size:13px;line-height:1.5;margin:6px 0 0;max-width:56ch}
.sec-warn{margin-top:12px;padding:12px 14px;border-radius:12px;background:#f0b35e1a;
  border:1px solid #f0b35e55;color:#f0c98a;font-size:13.5px;line-height:1.5}
.sec-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px}
.sec-actions .btn{padding:12px 16px;min-height:44px;font-size:14px}
.sec-step{background:var(--lift1);border:1px solid #1e293e;border-radius:12px;padding:16px;
  margin-top:14px;box-shadow:inset 0 1px 3px #00000059}
.sec-step h3,.once-h{font-family:var(--display);font-weight:400;font-size:20px;
  letter-spacing:.006em}
/* The security steps used to be <h4> under the card's <h2> — a level that was never
   announced to anyone: a screen reader jumped straight over h3. They are h3 now, and
   the rule follows them; the size and colour are the same ones the h4 had. */
.sec-step h3{margin:0 0 4px;font-size:15px;color:#f0f3fa}
.sec-step p{margin:0 0 12px;color:#98a2b5;font-size:13px;line-height:1.5}
.sec-flex{display:flex;gap:18px;flex-wrap:wrap;align-items:flex-start}
.sec-qr{background:#fff;border-radius:12px;padding:10px;width:186px;max-width:52vw;flex:0 0 auto;
  box-shadow:0 0 0 1px #d9b45b3d, var(--sh2)}
.sec-qr svg,.sec-qr img{width:100%;height:auto;display:block}
.sec-qr.empty{display:flex;align-items:center;justify-content:center;background:#0e1524;
  color:#98a2b5;font-size:12px;height:186px;text-align:center;border:1px dashed #28344d}
.sec-key{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:14px;
  background:#070910;border:1px solid #28344d;border-radius:9px;padding:11px 13px;color:#e8cd7d;
  word-break:break-all;line-height:1.45}
.sec-codes{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:8px;
  margin:10px 0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:14px}
.sec-codes span{background:#070910;border:1px solid #28344d;border-radius:8px;padding:9px 10px;
  text-align:center;color:#f0f3fa;letter-spacing:.06em}
.sec-input{width:100%;box-sizing:border-box;background:#070910;border:1px solid #28344d;
  color:#f0f3fa;border-radius:9px;padding:13px 14px;font-size:18px;min-height:50px;
  text-align:center;letter-spacing:.3em;font-variant-numeric:tabular-nums}
.sec-row-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:12px}
.sec-row-actions .btn{padding:12px 16px;min-height:44px;font-size:14px}
/* The desk's own panel lives in admin.html, where the card is an .adm-card and not a
   .sec-card — same contract (host toggles [hidden]), one rule so neither page has to
   remember which of the two it is. */
#adm-security [hidden]{display:none !important}

/* ---- the desk's own second step, said out loud on the statement page --------------
   The console can be closed; the statement page cannot. Whenever the desk opens a
   customer's book this strip is there with the truth about the desk's own sign-in:
   amber while their account has no second factor (or the state could not be read),
   green once it has one. It is never hidden for the desk — a warning that can be
   dismissed is a warning that gets dismissed — and `.desk-only` keeps it off every
   customer's screen, which the authorization test in the suite holds to. */
.sec-desk-warn{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  max-width:1120px;margin:0 auto var(--s3);padding:var(--s3) var(--s4);
  background:#131d33;border:1px solid #1e293e;border-radius:var(--r2);
  color:#b2bbcc;font-size:var(--t2);line-height:1.45}
.sec-desk-warn[hidden]{display:none}
.sec-desk-warn .sdw-dot{width:9px;height:9px;border-radius:50%;background:currentColor;flex:none}
.sec-desk-warn .sdw-txt{flex:1 1 260px;min-width:0}
.sec-desk-warn .sdw-link{flex:none;color:inherit;font-weight:700;white-space:nowrap;
  display:inline-flex;align-items:center;min-height:40px;padding:var(--s2) var(--s3);
  border:1px solid currentColor;border-radius:var(--r1);text-decoration:none}
.sec-desk-warn .sdw-link:hover{background:#ffffff14}
.sec-desk-warn.armed{background:#2a2113;border-color:#f0b35e66;color:#f0c98a}
.sec-desk-warn.armed .sdw-dot{animation:nrBlink 1.6s ease-in-out infinite}
.sec-desk-warn.ok{background:#12251c;border-color:#3ddc9744;color:#7ce3ae}

/* a thumb needs 44px, and the confirm fields in the two-factor modal were the only
   inputs on the site still without the standard padding */
.modal .btn{min-height:44px}
.modal input[type=password]{padding:13px 14px;font-size:16px}
@media(max-width:560px){
  .sec-flex{flex-direction:column}
  .sec-qr{width:100%;max-width:230px;margin:0 auto}
  .sec-actions .btn,.sec-row-actions .btn{flex:1 1 auto;justify-content:center;text-align:center}
}

/* ---- phone: the statement stops being a wide sheet ----------------------------
   Measured at 360px: the positions table was 1031px of columns inside a 316px
   window, so reading your own trade meant swiping three screens sideways. Below
   760px each row now prints as its own small block with the column name beside
   the number. Same markup, same numbers, same classes — and `screen` only, so
   Print and the server's own PDF/XLSX are untouched.

   The labels below repeat the <thead> in the same order. If a column is added or
   moved, its label has to move with it: tests/test_suite.py::TestStatementCards
   fails the suite if the two lists stop lining up. */
@media screen and (max-width:760px){
  .tbl-scroll{overflow-x:visible;border:0;background:none}
  table.st.cards{min-width:0;width:100%;table-layout:auto;font-size:12.5px}
  table.st.cards thead{display:none}
  table.st.cards tbody tr{display:block;margin:0 0 9px;padding:9px 11px;border-radius:11px;
    border:1px solid var(--paper-line);background:#fffdf7}
  table.st.cards tbody tr.voided-row{background:#fdf3f2;border-color:#e6c9c5}
  table.st.cards tbody tr.opening-row{background:#f6f3e6}
  /* the day band is a desktop reading aid: in the card layout every row already prints
     its own date, and the band would become a tall empty card (measured 226px). The
     selector is written at the card rule's own weight — `.st tr.day-band` alone loses
     to `table.st.cards tbody tr{display:block}`, which is how it first shipped. */
  table.st.cards tbody tr.day-band{display:none}
  table.st.cards tbody td{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
    border:0;padding:2px 0;text-align:right;white-space:normal;overflow-wrap:anywhere}
  /* A figure is never the thing that gives way. `overflow-wrap:anywhere` — kept on the
     prose cells, where a long reference number must still fit — lets a run of digits size
     down to a single character, so a squeezed line could break a number mid-figure
     (measured at 320px: "58,913." with "120" under it). On the numeric cells `break-word`
     wraps prose the same way but leaves the number's intrinsic width alone; the line wraps
     instead, and the `$ …` sub-line drops under its number the way it already reads on the
     sheet. `margin-right:auto` on the label keeps that dropped line hard against the right
     edge rather than back under the label. */
  table.st.cards tbody td.r{flex-wrap:wrap;justify-content:flex-end;overflow-wrap:break-word}
  table.st.cards tbody td.r>.sub{white-space:nowrap}
  table.st.cards tbody td.r::before{flex:0 1 auto;min-width:0;margin-right:auto}
  table.st.cards tbody td::before{max-width:52%;text-align:left;color:var(--paper-mut);
    font-size:11px;font-weight:600;letter-spacing:.02em}
  /* a cell with no label of its own (the actions cell) keeps the whole width */
  table.st.cards tbody td:not([class*="r"]) em,table.st.cards tbody td.acts{justify-content:flex-end}
  /* the "nothing here yet" line is one cell across the table — leave it as a line */
  table.st.cards tbody td[colspan]{display:block;text-align:center;padding:14px 0}
  table.st.cards tbody td[colspan]::before{content:none}

  /* ---- THE TWO TRADING BOOKS ARE TICKETS, NOT LISTS (10/10/2026) ----------------
     Until now a position on a phone was its fourteen columns read out one under the
     other — "#  1", "Type  Buy", "Swap (AED)  0.000" — three hundred pixels a trade,
     and the one figure a customer opens the page for (the P/L) buried ninth in the
     list. Measured on the frozen fixture: 24,263px of statement on a 390px phone.

     The same cells, the same DOM order (every test that reads a row by its cell index
     still reads the same cell), laid out as the ticket a trader would write: the
     reference and the day, then the trade itself — "Buy 3 TTB @ 4,587.000" — then the
     result on a line of its own in its colour with the dollar figure under it, then
     what it cost and what it is worth now, then the row's buttons.

     A grid of THREE columns, and why three: a grid shares its column widths across
     every row, so a wide figure in one row widens that column in all of them. The two
     measured columns only ever hold short things (a reference, a side, a quantity, a
     date); every long figure goes in the third, which takes whatever is left, or spans
     the first two. Measured at 320px and 390px: nothing overlaps, nothing wraps a
     number. The hidden cells (the serial, the "TTB" item) are the paper's; the rows
     keep them so the printed sheet and the tests are unchanged. */
  table.st.cards.cards-active tbody tr,table.st.cards.cards-closed tbody tr{
    display:grid;grid-template-columns:max-content max-content minmax(0,1fr);
    column-gap:10px;row-gap:4px;align-items:baseline;padding:10px 12px 9px}
  table.st.cards.cards-closed tbody tr.day-band{display:none}
  table.st.cards.cards-active tbody td,table.st.cards.cards-closed tbody td{
    display:inline-flex;flex-wrap:nowrap;align-items:baseline;gap:4px;padding:0;
    text-align:left;justify-content:flex-start;min-width:0;white-space:nowrap}
  table.st.cards.cards-active tbody td::before,table.st.cards.cards-closed tbody td::before{
    margin-right:0;max-width:none;flex:0 0 auto;white-space:nowrap}
  /* a figure with a `$ …` under it: the two stack inside the cell, the dirhams first */
  table.st.cards.cards-active tbody td.r,table.st.cards.cards-closed tbody td.r{
    display:block;white-space:nowrap}
  table.st.cards.cards-active tbody td.r::before,table.st.cards.cards-closed tbody td.r::before{
    display:inline;margin-right:4px}
  table.st.cards.cards-active tbody td .sub,table.st.cards.cards-closed tbody td .sub{
    display:block;margin:0;font-size:10.5px}

  /* open positions: # | Reference | Date | Type | Qty | Rate | Swap | Total | Current | P/L | acts */
  .cards-active td:nth-child(2){grid-column:1;grid-row:1;font-weight:700}
  .cards-active td:nth-child(2)::before{content:none}
  .cards-active td:nth-child(3){grid-column:2;grid-row:1;color:var(--paper-mut)}
  .cards-active td:nth-child(3)::before{content:none}
  .cards-active td:nth-child(1){grid-column:3;grid-row:1;justify-self:end;color:var(--paper-mut);font-size:11px}
  .cards-active td:nth-child(1)::before{content:"#";margin-right:1px}
  .cards-active td:nth-child(4){grid-column:1;grid-row:2;font-weight:700;font-size:14px}
  .cards-active td:nth-child(4)::before{content:none}
  .cards-active td:nth-child(5){grid-column:2;grid-row:2;font-weight:700;font-size:14px}
  .cards-active td:nth-child(5)::before{content:none}
  .cards-active td:nth-child(5)::after{content:" TTB";font-weight:500;color:var(--paper-mut);font-size:12px}
  .cards-active td:nth-child(6){grid-column:3;grid-row:2;font-size:14px}
  .cards-active td:nth-child(6)::before{content:"@";color:var(--paper-mut);font-weight:500}
  table.st.cards.cards-active tbody td:nth-child(10){grid-column:1 / 4;grid-row:3;display:flex;flex-wrap:wrap;
    justify-content:flex-end;text-align:right;font-size:16px;margin-top:2px}
  .cards-active td:nth-child(10)::before{content:"P/L";font-size:11px;margin-right:auto;align-self:baseline;color:var(--paper-mut)}
  .cards-active td:nth-child(8){grid-column:1 / 3;grid-row:4;font-size:12px}
  .cards-active td:nth-child(8)::before{content:"Cost"}
  .cards-active td:nth-child(9){grid-column:3;grid-row:4;font-size:12px}
  .cards-active td:nth-child(9)::before{content:"Now"}
  .cards-active td:nth-child(7){grid-column:1 / 4;grid-row:5;font-size:11.5px;color:var(--paper-mut)}
  .cards-active td:nth-child(7)::before{content:"Swap"}
  .cards-active td:nth-child(11){grid-column:1 / 4;grid-row:6}
  .cards-active td:nth-child(11)::before{content:none}

  /* closed trades: SL | Item | Open date | Qty | B/S | Open rate | Closed date | B/S | Close rate
                    | Swap | Total | Current | Gross | Charge | P/L (+ the desk's chips) */
  table.st.cards.cards-closed tbody td:nth-child(1),
  table.st.cards.cards-closed tbody td:nth-child(2){display:none}   /* the serial, "TTB": paper things */
  .cards-closed td:nth-child(3){grid-column:1 / 3;grid-row:1;color:var(--paper-mut)}
  .cards-closed td:nth-child(3)::before{content:"Opened";font-weight:500}
  .cards-closed td:nth-child(7){grid-column:3;grid-row:1;justify-self:end;color:var(--paper-mut)}
  .cards-closed td:nth-child(7)::before{content:"Closed";font-weight:500}
  .cards-closed td:nth-child(5){grid-column:1;grid-row:2;font-weight:700;font-size:14px}
  .cards-closed td:nth-child(5)::before{content:none}
  .cards-closed td:nth-child(4){grid-column:2;grid-row:2;font-weight:700;font-size:14px}
  .cards-closed td:nth-child(4)::before{content:none}
  .cards-closed td:nth-child(4)::after{content:" TTB";font-weight:500;color:var(--paper-mut);font-size:12px}
  .cards-closed td:nth-child(6){grid-column:3;grid-row:2;font-size:14px}
  .cards-closed td:nth-child(6)::before{content:"@";color:var(--paper-mut);font-weight:500}
  .cards-closed td:nth-child(8){grid-column:1;grid-row:3;font-weight:700;font-size:14px}
  .cards-closed td:nth-child(8)::before{content:none}
  .cards-closed td:nth-child(9){grid-column:2 / 4;grid-row:3;font-size:14px}
  .cards-closed td:nth-child(9)::before{content:"@";color:var(--paper-mut);font-weight:500}
  /* the result, on a line of its own, and the desk's chips under it (the `.fix-btns`
     rule below gives them the line and the thumb-sized buttons) */
  table.st.cards.cards-closed tbody td:nth-child(15){grid-column:1 / 4;grid-row:4;display:flex;flex-wrap:wrap;
    justify-content:flex-end;text-align:right;font-size:16px;margin-top:2px}
  .cards-closed td:nth-child(15)::before{content:"Net P/L";font-size:11px;margin-right:auto;align-self:baseline;color:var(--paper-mut)}
  .cards-closed td:nth-child(13){grid-column:1 / 3;grid-row:5;font-size:12px}
  .cards-closed td:nth-child(13)::before{content:"Gross"}
  .cards-closed td:nth-child(14){grid-column:3;grid-row:5;font-size:12px}
  .cards-closed td:nth-child(14)::before{content:"Charge"}
  .cards-closed td:nth-child(11){grid-column:1 / 3;grid-row:6;font-size:12px}
  .cards-closed td:nth-child(11)::before{content:"Cost"}
  .cards-closed td:nth-child(12){grid-column:3;grid-row:6;font-size:12px}
  .cards-closed td:nth-child(12)::before{content:"Sold"}
  .cards-closed td:nth-child(10){grid-column:1 / 4;grid-row:7;font-size:11.5px;color:var(--paper-mut)}
  .cards-closed td:nth-child(10)::before{content:"Swap"}

  .cards-void td:nth-child(1)::before{content:"Reference"}
  .cards-void td:nth-child(2)::before{content:"Type"}
  .cards-void td:nth-child(3)::before{content:"Quantity"}
  .cards-void td:nth-child(4)::before{content:"Rate"}
  .cards-void td:nth-child(5)::before{content:"Reversed"}
  .cards-void td:nth-child(6)::before{content:"By"}
  .cards-void td:nth-child(7)::before{content:"Reason"}

  .cards-daily td:nth-child(1)::before{content:"Date"}
  .cards-daily td:nth-child(2)::before{content:"Reference"}
  .cards-daily td:nth-child(3)::before{content:"TT bars at 12:59"}
  .cards-daily td:nth-child(4)::before{content:"Rate (AED / bar)"}
  .cards-daily td:nth-child(5)::before{content:"Total (AED)"}
  .cards-daily td:nth-child(6)::before{content:"Status";flex:0 0 auto}
  /* a reversed day carries its tag and the reason: they wrap under the label, the label
     and the tag never break mid-word */
  .cards-daily td:nth-child(6){flex-wrap:wrap;text-align:right}
  .cards-daily .tag-voided{white-space:nowrap}

  .cards-order td:nth-child(1)::before{content:"Ref"}
  .cards-order td:nth-child(2)::before{content:"Side"}
  .cards-order td:nth-child(3)::before{content:"Quantity"}
  .cards-order td:nth-child(4)::before{content:"Price ($/oz)"}
  .cards-order td:nth-child(5)::before{content:"Status"}
  .cards-order td:nth-child(6)::before{content:"Placed"}
  .cards-order td:nth-child(7)::before{content:"Note"}
  .cards-order td:nth-child(8)::before{content:none}

  .cards-ledger td:nth-child(1)::before{content:"Doc Date"}
  .cards-ledger td:nth-child(2)::before{content:"Type"}
  .cards-ledger td:nth-child(3)::before{content:"Doc. No."}
  .cards-ledger td:nth-child(4)::before{content:"Narration"}
  .cards-ledger td:nth-child(5)::before{content:"Debit AED"}
  .cards-ledger td:nth-child(6)::before{content:"Credit AED"}
  .cards-ledger td:nth-child(7)::before{content:"Balance AED"}
  .cards-ledger td:nth-child(8)::before{content:"Gold debit (gms)"}
  .cards-ledger td:nth-child(9)::before{content:"Gold credit (gms)"}
  .cards-ledger td:nth-child(10)::before{content:"Gold balance (gms)"}

  /* ---- the ledger entry is a ticket too (10/10/2026) ----------------------------
     The ten columns of the supplier's ledger read as a list left half the lines
     empty — "Credit AED", "Gold debit (gms)" with nothing after them — and the
     Narration label broke mid-word ("Narrati on") because the label was the thing
     that shrank. Now: the day, the type and the document number on one line, the
     narration on its own, then only the money that moved with the balance after it
     in bold at the end of the line, and the grams the same way underneath. A cell
     with nothing in it is not drawn at all. Same three-column rule as the books:
     the short things in the two measured columns, every figure spanning or in the
     third. The debit and the credit have a row each (a row with nothing in it is
     nothing tall), and the balance stands beside both. */
  table.st.cards.cards-ledger tbody tr{display:grid;grid-template-columns:max-content max-content minmax(0,1fr);
    column-gap:10px;row-gap:3px;align-items:baseline;padding:10px 12px 9px}
  table.st.cards.cards-ledger tbody tr.day-band{display:none}
  table.st.cards.cards-ledger tbody td{display:inline-flex;flex-wrap:nowrap;align-items:baseline;gap:5px;
    padding:0;text-align:left;justify-content:flex-start;min-width:0;white-space:nowrap}
  table.st.cards.cards-ledger tbody td::before{margin-right:0;max-width:none;flex:0 0 auto;white-space:nowrap}
  table.st.cards.cards-ledger tbody td.r{display:inline-flex;white-space:nowrap}
  table.st.cards.cards-ledger tbody td:empty{display:none}
  .cards-ledger td:nth-child(1){grid-column:1;grid-row:1;font-weight:700}
  .cards-ledger td:nth-child(1)::before{content:none}
  .cards-ledger td:nth-child(2){grid-column:2;grid-row:1;color:var(--paper-mut)}
  .cards-ledger td:nth-child(2)::before{content:none}
  .cards-ledger td:nth-child(3){grid-column:3;grid-row:1;justify-self:end;color:var(--paper-mut);font-size:11.5px}
  .cards-ledger td:nth-child(3)::before{content:none}
  table.st.cards.cards-ledger tbody td:nth-child(4){grid-column:1 / 4;grid-row:2;white-space:normal;justify-self:start;
    overflow-wrap:anywhere;display:block;text-align:left}
  .cards-ledger td:nth-child(4)::before{content:none}
  .cards-ledger td:nth-child(5){grid-column:1 / 3;grid-row:3}
  .cards-ledger td:nth-child(5)::before{content:"Debit"}
  .cards-ledger td:nth-child(6){grid-column:1 / 3;grid-row:4}
  .cards-ledger td:nth-child(6)::before{content:"Credit"}
  .cards-ledger td:nth-child(7){grid-column:3;grid-row:3 / 5;justify-self:end;align-self:start}
  .cards-ledger td:nth-child(7)::before{content:"Balance"}
  .cards-ledger td:nth-child(8){grid-column:1 / 3;grid-row:5;font-size:12px}
  .cards-ledger td:nth-child(8)::before{content:"Gold Dr"}
  .cards-ledger td:nth-child(9){grid-column:1 / 3;grid-row:6;font-size:12px}
  .cards-ledger td:nth-child(9)::before{content:"Gold Cr"}
  .cards-ledger td:nth-child(10){grid-column:3;grid-row:5 / 7;justify-self:end;align-self:start;font-size:12px}
  .cards-ledger td:nth-child(10)::before{content:"Gold"}

  /* touch targets: 44px is the size a thumb actually hits (Apple's own minimum).
     Measured before this rule: the header buttons were 24px tall, the big Buy/Sell
     43px, the row actions 20px. */
  .top-right .btn,.btn.small,.x,.mid,button.big,.step{min-height:44px}
  .top-right .btn{display:inline-flex;align-items:center}
  .x{min-width:44px;display:inline-flex;align-items:center;justify-content:center}
  .mid{display:inline-flex;align-items:center;justify-content:center}
  button.big{display:inline-flex;align-items:center;justify-content:center}
  /* the row's own buttons ("Sell", "Reminder", "Correct") were 40x20 */
  td.mini button,.j-item button,#journal button{min-height:44px;min-width:44px}
  table.st.cards tbody td.mini{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;padding-top:8px}
  /* The closed book has no actions cell — its desk buttons (Correct / Reverse / Re-open)
     are built into the P/L cell. In a card they are a row ACTION, not part of the money
     line: the wrapper takes a line of its own under the figures, away from the P/L number
     and its `$ …` sub-figure, and the buttons get the same 44px thumb floor as every
     other row action. (Before this rule Re-open was an unstyled browser button, 55x19px,
     riding the money line.) */
  table.st.cards.cards-closed tbody td>.fix-btns{flex:1 1 100%;justify-content:flex-end;margin-top:8px}
  table.st.cards.cards-closed tbody td>.fix-btns button{min-height:44px;min-width:44px}
  .filter-bar .btn,.filter-bar select,.filter-bar input{min-height:44px}
}

/* ---- the narrowest phones (10/10/2026) -------------------------------------------
   At 320px the card is 252px wide inside its paddings, and a three-column grid whose
   measured columns hold a reference and a date leaves the third too narrow for a
   figure like "Now 288,840.633" (measured: 8px short, and the ticket then scrolled
   sideways inside its own box). Below 360px every long figure takes a line of its
   own: a card is a few lines taller and nothing is cut. */
@media screen and (max-width:360px){
  /* the serial is hidden and the date takes its place at the end of the first line,
     so the second measured column holds only the quantity and the price gets 30px more */
  table.st.cards.cards-active tbody td:nth-child(1){display:none}
  .cards-active td:nth-child(3){grid-column:2 / 4;justify-self:end}
  .cards-active td:nth-child(8){grid-column:1 / 4;grid-row:4}
  .cards-active td:nth-child(9){grid-column:1 / 4;grid-row:5}
  .cards-active td:nth-child(7){grid-row:6}
  .cards-active td:nth-child(11){grid-row:7}
  .cards-closed td:nth-child(3){grid-column:1 / 4;grid-row:1;justify-self:start}
  .cards-closed td:nth-child(7){grid-column:1 / 4;grid-row:2;justify-self:start}
  .cards-closed td:nth-child(5),.cards-closed td:nth-child(4),.cards-closed td:nth-child(6){grid-row:3}
  .cards-closed td:nth-child(8),.cards-closed td:nth-child(9){grid-row:4}
  table.st.cards.cards-closed tbody td:nth-child(15){grid-row:5}
  .cards-closed td:nth-child(13),.cards-closed td:nth-child(14){grid-row:6}
  .cards-closed td:nth-child(11){grid-column:1 / 4;grid-row:7}
  .cards-closed td:nth-child(12){grid-column:1 / 4;grid-row:8}
  .cards-closed td:nth-child(10){grid-row:9}
  .cards-ledger td:nth-child(7){grid-column:1 / 4;grid-row:5;justify-self:end}
  .cards-ledger td:nth-child(8){grid-row:6}
  .cards-ledger td:nth-child(9){grid-row:7}
  .cards-ledger td:nth-child(10){grid-column:1 / 4;grid-row:8;justify-self:end}
}

/* The card block above has to undo three rules that were written for the wide printed
   sheet: the ledger table's 1020px floor, its narration column pinned to 420px, and
   the zebra striping that painted every even row's cells. Without these the ledger
   stayed 444px wide and pushed the whole page sideways on a 360px phone. */
@media screen and (max-width:760px){
  table.st.ledg{min-width:0}
  .st.cards td.wrap,.st.ledg.cards td.wrap{width:auto;min-width:0;max-width:none;height:auto}
  .st.ledg.cards tbody tr{height:auto}
  table.st.cards tbody tr td{background:none}
}

/* ============================================================================
   Moved out of per-page <style> blocks, 14/09/2026.
   login.html and admin.html each carried a <style> block and a handful of
   style="…" attributes. With CSP moving to `style-src 'self'` (no
   'unsafe-inline') every one of those had to become a rule in this file. The
   declarations are copied verbatim and the selectors keep the specificity they
   had as inline styles, so nothing on either page renders differently.
   ============================================================================ */

/* --- the welcome page (login.html) ---
   The sign-in page is the first and sometimes the only thing a customer sees, so it
   carries the whole identity by itself. Everything is a rule in this sheet — the page
   has no <style> block (style-src 'self') — and the one image is the desk's own
   vector bullion, drawn by scripts/make_bullion_art.py and served from this origin.

   THE LAYOUT IS ONE GRID WITH THREE BLOCKS, and the areas are what make the phone
   version a design rather than a squeeze:

       phone            desk (>=980px)
       ┌───────┐        ┌─────────┬───────┐
       │ brand │        │  brand  │       │
       ├───────┤        ├─────────┤ form  │
       │ form  │        │   art   │       │
       ├───────┤        └─────────┴───────┘
       │  art  │
       └───────┘

   On a phone the form is SECOND — masthead, the way in, then the metal. Nobody has
   to scroll past the artwork to reach the password field.

   THE ROOM IS LIT, 17/09/2026. The page used to be a still life: three flat blocks on
   a black field. It is now a room with light moving in it — a gold aurora that drifts,
   motes in the air, an engine-turned rosette that turns once every two minutes, metal
   that floats on its own shadow with the light walking across it, and a card of smoked
   glass with a rim of the alloy running round it. Every one of those is a gradient or
   a pseudo-element: no image was added, no font was added, and nothing on the page is
   fetched from anywhere but this origin.

   NONE OF IT IS LOAD-BEARING. The four controls, their ids, the two steps and the
   script that drives them are exactly what they were. The whole of the motion above
   is switched off in one place — `prefers-reduced-motion: reduce`, which this sheet
   already honours globally — and the page is then the same page, still.  */

/* The one custom property that has to be interpolable: a conic gradient's start angle
   cannot be animated as a plain --var (a custom property is a string until it is
   registered), which is why the card's rim needs this declaration to move at all.
   A browser too old for @property drops the conic rule as invalid and keeps the plain
   1px border under it — the card simply stops shimmering. */
@property --nr-spin { syntax: "<angle>"; initial-value: 0deg; inherits: false; }

/* The sign-in page keeps its own light on top of the room's: a broader, warmer
   bloom behind the metal, because on this one page the metal IS the subject. */
.login-wrap { position:relative; isolation:isolate; overflow:hidden;
  min-height:100vh; min-height:100dvh; display:flex; align-items:center;
  justify-content:center; padding:clamp(20px,4.5vw,56px) clamp(16px,3.4vw,48px); }
/* the aurora: the same three lights the page always had, now on a 34-second drift.
   It is one composited pseudo-element that never touches layout, and it is scaled up
   1.35x so the blobs can travel without their edges ever entering the frame. */
.login-wrap::before { content:""; position:absolute; inset:-18%; z-index:0;
  pointer-events:none; transform-origin:60% 40%;
  background:
    radial-gradient(1150px 660px at 70% 10%, #d9b45b24, transparent 64%),
    radial-gradient(820px 520px at 6% 88%, #24365c47, transparent 72%),
    radial-gradient(700px 360px at 46% 98%, #d9b45b18, transparent 74%);
  animation:nrAurora 34s ease-in-out infinite; }
@keyframes nrAurora {
  0%,100% { transform:translate3d(0,0,0) scale(1); }
  33%     { transform:translate3d(2.2%,-1.8%,0) scale(1.07); }
  66%     { transform:translate3d(-2%,1.6%,0) scale(1.03); }
}
/* and a vignette over it, so the middle of the page is where the eye lands */
.login-wrap::after { content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:radial-gradient(120% 90% at 50% 45%, transparent 52%, #03050a80 100%); }
.welcome { position:relative; z-index:1;
  width:100%; max-width:1260px; display:grid; justify-items:center;
  align-items:start; gap:var(--s5) var(--s7);
  grid-template-columns:minmax(0,1fr);
  grid-template-areas:"brand" "form" "art"; }
.wl-brand { grid-area:brand; }
.login-card { grid-area:form; }
.wl-art { grid-area:art; }
.wl-brand, .wl-art { width:100%; max-width:430px; }

/* ---- the air: fourteen motes of gold, and not one image ----
   Each <i> is a 2–4px speck of the alloy given its own lane, its own size and its own
   clock by the :nth-child rules below, so nothing on the page beats in time with
   anything else. They rise the height of the screen and fade out at both ends; they
   are inside `overflow:hidden`, are never hit-tested, and are aria-hidden. */
.nr-dust { position:absolute; inset:0; z-index:0; pointer-events:none; overflow:hidden; }
.nr-dust i { position:absolute; bottom:-6%; width:3px; height:3px; border-radius:50%;
  opacity:0; will-change:transform,opacity;
  background:radial-gradient(circle at 50% 50%, #fdf3d6, #d9b45b 42%, #d9b45b00 72%);
  animation:nrDust linear infinite; }
@keyframes nrDust {
  0%   { opacity:0; transform:translate3d(0,0,0) scale(.55); }
  10%  { opacity:.8; }
  70%  { opacity:.45; }
  100% { opacity:0; transform:translate3d(26px,-108vh,0) scale(1.15); }
}
.nr-dust i:nth-child(1)  { left:6%;  animation-duration:26s; animation-delay:-2s;  }
.nr-dust i:nth-child(2)  { left:13%; animation-duration:33s; animation-delay:-11s; width:2px; height:2px; }
.nr-dust i:nth-child(3)  { left:21%; animation-duration:21s; animation-delay:-17s; }
.nr-dust i:nth-child(4)  { left:28%; animation-duration:38s; animation-delay:-6s;  width:4px; height:4px; }
.nr-dust i:nth-child(5)  { left:35%; animation-duration:29s; animation-delay:-23s; width:2px; height:2px; }
.nr-dust i:nth-child(6)  { left:42%; animation-duration:24s; animation-delay:-14s; }
.nr-dust i:nth-child(7)  { left:49%; animation-duration:41s; animation-delay:-30s; width:2px; height:2px; }
.nr-dust i:nth-child(8)  { left:57%; animation-duration:27s; animation-delay:-4s;  }
.nr-dust i:nth-child(9)  { left:64%; animation-duration:35s; animation-delay:-19s; width:4px; height:4px; }
.nr-dust i:nth-child(10) { left:71%; animation-duration:23s; animation-delay:-9s;  width:2px; height:2px; }
.nr-dust i:nth-child(11) { left:78%; animation-duration:31s; animation-delay:-26s; }
.nr-dust i:nth-child(12) { left:85%; animation-duration:19s; animation-delay:-13s; width:2px; height:2px; }
.nr-dust i:nth-child(13) { left:91%; animation-duration:37s; animation-delay:-21s; }
.nr-dust i:nth-child(14) { left:96%; animation-duration:28s; animation-delay:-7s;  width:2px; height:2px; }

/* ---- the masthead ---- */
.wl-brand { display:flex; flex-direction:column; align-items:flex-start; gap:var(--s3); }
/* the hallmark: the monogram inside a ring that turns once every twenty-six seconds.
   An assay mark is a struck circle round a maker's letters, and that is the whole
   idea — the dashes are the knurl, and they are the only thing in motion. */
.wl-mark { position:relative; width:88px; height:88px; display:grid; place-items:center; flex:none; }
.wl-ring { position:absolute; inset:0; border-radius:50%;
  border:1px dashed #d9b45b52; animation:nrTurn 26s linear infinite; }
.wl-ring::after { content:""; position:absolute; inset:5px; border-radius:50%;
  border:1px solid #d9b45b24; }
@keyframes nrTurn { to { transform:rotate(360deg); } }
/* the eyebrow is a rule and a line of small caps: the mark of a letterhead, and
   the thing that stops the wordmark below it floating in the middle of nothing */
.wl-eyebrow { margin:0; color:var(--gold2); font-size:var(--t1); font-weight:700;
  text-transform:uppercase; letter-spacing:.22em; display:flex; align-items:center;
  gap:var(--s3); }
.wl-eyebrow { align-self:stretch; }
.wl-eyebrow::after { content:""; flex:1 1 auto; height:1px; min-width:24px;
  background:linear-gradient(90deg,#d9b45b73,transparent 88%); }
/* The desk's name, and the one place on the site the display face is given its
   full size. A flat colour, never a gradient clipped to the text — a transparent
   fill measures 1:1 to every contrast tool there is, including ours. */
.wl-brand h1 { margin:0; font-family:var(--display); font-weight:400;
  font-size:clamp(36px,6.4vw,62px); line-height:1.0; letter-spacing:-.005em;
  color:#fbf6ea; text-shadow:0 1px 0 #00000080, 0 16px 60px #d9b45b33; }
.wl-lead { margin:0; max-width:50ch; color:#bcc8de; font-size:clamp(14.5px,1.35vw,17px);
  line-height:1.62; }

/* ---- the metal, and the three facts under it ---- */
.wl-art { display:flex; flex-direction:column; gap:var(--s4); }
/* The bars stand in three layers, and the order is the order of a photograph: the
   engraved plate behind them, the light they are lit by, the metal, and last the
   sheen that walks across it. */
.wl-shot { margin:0; position:relative; isolation:isolate;
  transform:translate3d(calc(var(--nr-ax,0) * 1px), calc(var(--nr-ay,0) * 1px), 0);
  transition:transform 420ms cubic-bezier(.22,.61,.36,1); }
/* exactly the width of the figure it sits behind, never a pixel more: at 112% it
   hung 6% past each edge and dragged a 390px phone sideways by 5px (measured by
   scripts/visual_check.py, which is the whole reason that check exists).
   It turns — one revolution every two minutes, which is slow enough that it is
   never caught moving and never quite where it was. */
.wl-shot::before { content:""; position:absolute; left:50%; top:46%;
  width:min(100%,600px); aspect-ratio:1; transform:translate(-50%,-50%);
  pointer-events:none; opacity:.055; z-index:0;
  background:url(/guilloche.svg) center/contain no-repeat;
  animation:nrRosette 120s linear infinite; }
@keyframes nrRosette { to { transform:translate(-50%,-50%) rotate(360deg); } }
/* the light the bars are lit by, breathing on the same seven-and-a-half seconds the
   metal floats on — so the bloom swells as the bars rise and settles as they fall */
.wl-shot::after { content:""; position:absolute; left:50%; top:50%; z-index:0;
  width:74%; height:62%; transform:translate(-50%,-50%); pointer-events:none;
  border-radius:50%; filter:blur(6px);
  background:radial-gradient(50% 50% at 50% 50%, #d9b45b2e, #d9b45b0f 46%, transparent 72%);
  animation:nrBreathe 7.5s ease-in-out infinite; }
@keyframes nrBreathe {
  0%,100% { opacity:.72; transform:translate(-50%,-50%) scale(1); }
  50%     { opacity:1;   transform:translate(-50%,-50%) scale(1.07); }
}
/* the shadow the bars cast on the desk: it tightens and darkens as they come down,
   which is the whole of what makes them read as lifted rather than as pasted on.
   Two layers: the dark core the bars sit in, and the warm pool of their own light
   spilling past it onto the desk. */
.wl-cast { position:absolute; left:50%; bottom:6%; z-index:0; width:64%; height:44px;
  pointer-events:none; filter:blur(12px);
  background:
    radial-gradient(46% 50% at 50% 50%, #000000b8, #00000059 52%, transparent 74%),
    radial-gradient(66% 72% at 50% 50%, #d9b45b33, #d9b45b12 48%, transparent 72%);
  animation:nrCast 7.5s ease-in-out infinite; }
@keyframes nrCast {
  0%,100% { opacity:.9; transform:translate(-50%,0) scale(1); }
  50%     { opacity:.55; transform:translate(-50%,0) scale(.88); }
}
/* width/height are on the <img> so the box is reserved before the file arrives and
   nothing on the page moves when it does */
.wl-shot img { position:relative; z-index:1;
  display:block; width:100%; height:auto; max-width:560px; margin:0 auto;
  animation:nrFloat 7.5s ease-in-out infinite; }
@keyframes nrFloat {
  0%,100% { transform:translateY(0); }
  50%     { transform:translateY(-13px); }
}
/* The sheen is a single band of white light dragged across the figure once every
   eleven seconds — and it is masked by the artwork's OWN alpha, so it lands on the
   bars and on nothing else. `screen` is what makes it light rather than paint: it
   can only ever add to what is under it, so the dark facets stay dark. */
.wl-sheen { position:absolute; inset:0; z-index:2; pointer-events:none; opacity:0;
  -webkit-mask:url(/gold-bullion.svg) center/contain no-repeat;
  mask:url(/gold-bullion.svg) center/contain no-repeat;
  background:linear-gradient(102deg, transparent 40%, #fff6dfa6 47%, #fffdf5 50%,
    #fff6dfa6 53%, transparent 60%);
  background-size:280% 100%; background-repeat:no-repeat;
  mix-blend-mode:screen; animation:nrSheen 11s ease-in-out infinite; }
@keyframes nrSheen {
  0%, 58%   { background-position:180% 0; opacity:0; }
  63%       { opacity:.85; }
  84%       { opacity:.85; }
  92%, 100% { background-position:-70% 0; opacity:0; }
}
/* the three facts, struck as assay chips rather than set as a row of captions: a
   hairline box each, a little light along the top, and the alloy on the rim when the
   pointer is over one */
.wl-facts { list-style:none; margin:0; padding:var(--s2) 0 0; display:grid;
  grid-template-columns:repeat(auto-fit,minmax(112px,1fr)); gap:var(--s3); }
.wl-facts li { display:flex; flex-direction:column; gap:3px; min-width:0;
  padding:var(--s3) var(--s4); border-radius:var(--r2);
  background:linear-gradient(180deg,#ffffff0d,#ffffff03);
  border:1px solid #ffffff17; box-shadow:inset 0 1px 0 #ffffff0f;
  transition:border-color var(--slow) var(--ease), transform var(--slow) var(--ease),
    box-shadow var(--slow) var(--ease), background var(--slow) var(--ease); }
.wl-facts li:hover { transform:translateY(-3px); border-color:#d9b45b5c;
  background:linear-gradient(180deg,#d9b45b14,#ffffff04);
  box-shadow:inset 0 1px 0 #ffffff1a, 0 14px 30px -18px #d9b45b99; }
.wl-facts b { font-family:var(--display); font-size:26px; font-weight:400;
  color:var(--gold2); letter-spacing:.01em; line-height:1.1; }
.wl-facts span { font-size:var(--t2); color:#a7b4cd; letter-spacing:.01em;
  line-height:1.35; }

/* ---- the card: the four controls, unchanged ----
   Smoked glass over the room's own light: the fill is dark and slightly transparent,
   and `backdrop-filter` blurs whatever of the aurora, the motes and the grain passes
   behind it. A browser without backdrop-filter renders the same card without the
   blur — the fill is opaque enough to carry it on its own. */
.login-card { width:100%; max-width:440px; position:relative; z-index:1;
  background:
    radial-gradient(520px 230px at 50% -14%, #d9b45b1f, transparent 70%),
    linear-gradient(170deg, #1a2440e6 0%, #101a2ef0 46%, #0a0f1cf7 100%);
  -webkit-backdrop-filter:blur(20px) saturate(135%);
  backdrop-filter:blur(20px) saturate(135%);
  border:1px solid #33456b; border-radius:26px;
  padding:var(--s6) var(--s6) var(--s5);
  box-shadow:0 2px 6px #00000073, 0 34px 80px -28px #000000e0,
    0 0 70px -30px #d9b45b4d, inset 0 1px 0 #ffffff1c; }
/* the rim: one turn of the alloy round the edge of the card, every sixteen seconds.
   It is a conic gradient masked to a 1px frame — the mask keeps the middle of the
   pseudo-element cut out, so what is left is a border and not a fill. */
.login-card::before { content:""; position:absolute; inset:-1px; border-radius:inherit;
  padding:1px; pointer-events:none; z-index:0;
  background:conic-gradient(from var(--nr-spin),
    #d9b45b00 0deg, #d9b45b00 46deg, #f0d48ab3 84deg, #fffaec 100deg, #f0d48ab3 116deg,
    #d9b45b00 160deg, #d9b45b00 226deg, #f0d48a8c 264deg, #fdf3d6cc 280deg,
    #f0d48a8c 296deg, #d9b45b00 336deg, #d9b45b00 360deg);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite:exclude;
  animation:nrRim 16s linear infinite; }
@keyframes nrRim { to { --nr-spin:360deg; } }
/* one hairline of the desk's own gold along the top edge of the card — the same
   line the statement's masthead draws under the letterhead */
.login-card::after { content:""; position:absolute; left:18%; right:18%; top:-1px;
  height:1px; border-radius:1px; z-index:2; pointer-events:none;
  background:linear-gradient(90deg,transparent,#f0d48ab3,transparent); }
/* the light the pointer carries across the glass. login-fx.js writes --nr-px/--nr-py
   on the card; with that file absent the fallbacks hold it at the top middle, which
   is exactly where the card's own key light is. */
.lg-glow { position:absolute; inset:0; z-index:0; pointer-events:none;
  border-radius:inherit; opacity:.9;
  background:radial-gradient(420px 320px at var(--nr-px,50%) var(--nr-py,0%),
    #d9b45b1f, transparent 68%);
  transition:opacity 300ms var(--ease); }
/* everything the customer actually reads sits above both of those */
.login-card > * { position:relative; z-index:1; }
.login-logo { width:66px; height:66px; border-radius:50%; margin:0;
  display:flex; align-items:center; justify-content:center; flex:none;
  font-family:var(--display); font-size:30px; font-weight:400; letter-spacing:.03em;
  color:var(--gold2);
  background:radial-gradient(circle at 42% 22%, #2b3a5e, #0b1120 76%);
  border:1.5px solid var(--gold);
  box-shadow:0 0 0 1px #00000073, 0 0 0 7px #d9b45b14, 0 14px 34px #00000099,
    inset 0 2px 2px #ffffff26, inset 0 -3px 8px #d9b45b1a; }
.login-card h2 { text-align:center; font-family:var(--display); font-size:30px;
  font-weight:400; letter-spacing:.008em; line-height:1.15;
  margin:0 0 var(--s2); color:#fbf6ea; }
.login-card .login-sub { text-align:center; color:var(--mut); font-size:var(--t2);
  margin:0; }
/* the flourish: a ruled line broken by a struck lozenge — the ornament a certificate
   puts between its title and its body, and the one piece of decoration inside the
   card. Two pseudo-elements of the <header>, so the markup stays four controls. */
.login-card header { position:relative; padding-bottom:var(--s6); }
.login-card header::before { content:""; position:absolute; left:50%; bottom:15px;
  transform:translateX(-50%); width:124px; height:1px;
  background:linear-gradient(90deg, transparent, #d9b45b7a 24%, transparent 44%,
    transparent 56%, #d9b45b7a 76%, transparent); }
.login-card header::after { content:""; position:absolute; left:50%; bottom:12px;
  width:7px; height:7px; border-radius:1px; transform:translateX(-50%) rotate(45deg);
  background:linear-gradient(135deg,#fdf3d6,#d9b45b 55%,#8a6a1f);
  box-shadow:0 0 12px #d9b45b73; }
.lg-field { margin:0 0 var(--s4); }
.login-card label { display:block; font-size:var(--t2); font-weight:600; color:#bcc8de;
  letter-spacing:.01em; margin:0 0 var(--s2);
  transition:color var(--fast) var(--ease); }
.lg-field:focus-within label { color:var(--gold2); }
.login-card input { width:100%; box-sizing:border-box;
  background:linear-gradient(180deg,#070a12,#0a0f1b);
  border:1px solid var(--line2);
  color:var(--txt); border-radius:14px; padding:var(--s3) var(--s4); font-size:var(--t3);
  min-height:54px; outline:0;
  transition:border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease),
    background var(--fast) var(--ease); }
.login-card input::placeholder { color:#626e86; }
.login-card input:focus { border-color:var(--gold); background:#0a0f1b;
  box-shadow:0 0 0 3px #d9b45b29, 0 10px 30px -14px #d9b45b8c, inset 0 1px 0 #ffffff0d; }
/* the field that was refused: the border says which one, the line below says why */
.lg-bad input { border-color:var(--down); box-shadow:0 0 0 3px #ff7a7021; }
/* ---- what is inside the box: the mark on the left, the eye on the right ----
   The two icons are the only thing that tells the fields apart at a glance, and they
   are drawn in the page, not fetched. Neither is focusable and neither is read: the
   <label> above the field is what names it, exactly as before. */
.lg-box { position:relative;
  transition:transform var(--fast) var(--ease); }
.lg-box:focus-within { transform:translateY(-1px); }
.lg-ico { position:absolute; left:15px; top:50%; transform:translateY(-50%);
  width:20px; height:20px; color:#66748f; pointer-events:none;
  transition:color var(--slow) var(--ease); }
.lg-ico svg { width:100%; height:100%; display:block; }
.lg-box:focus-within .lg-ico { color:var(--gold2); }
.lg-box input { padding-left:47px; }
.lg-field.pw input { padding-right:58px; }
/* the password carries a reveal button — a long password typed on a phone keyboard
   should be checkable instead of retyped */
.lg-eye { position:absolute; top:50%; right:var(--s2); transform:translateY(-50%);
  width:44px; height:44px; padding:0; display:inline-flex; align-items:center; justify-content:center;
  background:none; border:0; border-radius:var(--r1); color:var(--mut); cursor:pointer;
  transition:color var(--fast) var(--ease), background var(--fast) var(--ease); }
.lg-eye:hover { color:var(--gold2); background:#ffffff0a; }
.lg-eye svg { width:20px; height:20px; display:block; }
.lg-eye .i-off { display:none; }
.lg-eye.shown .i-on { display:none; }
.lg-eye.shown .i-off { display:block; }
/* Caps Lock: a dark password field cannot show it and the browser will not say it.
   Written by login-fx.js, and it takes no room at all until there is something to say. */
.lg-caps { margin:var(--s2) 0 0; color:#f0c98a; font-size:var(--t2); font-weight:600;
  letter-spacing:.01em; animation:nrStepIn var(--slow) var(--ease) both; }
.login-card .btn { width:100%; }
.login-card .btn.block { margin-top:var(--s2); }
/* ---- the way in: a struck button with the light still moving on it ----
   The sweep is a pseudo-element, never a child: login.js replaces this button's whole
   text content while it waits on the server (`btn.textContent = label`), and any real
   element inside it would be destroyed the first time somebody signed in. */
.login-card .btn.gold.block { position:relative; overflow:hidden; min-height:54px;
  border-radius:14px; font-size:var(--t3); letter-spacing:.012em;
  transition:background var(--fast) var(--ease), border-color var(--fast) var(--ease),
    transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease); }
.login-card .btn.gold.block::after { content:""; position:absolute; top:-60%; bottom:-60%;
  left:0; width:36%; pointer-events:none;
  background:linear-gradient(90deg,#ffffff00,#ffffffb8,#ffffff00);
  animation:nrShine 5.2s ease-in-out infinite; }
@keyframes nrShine {
  0%, 62%   { transform:translateX(-160%) skewX(-18deg); }
  88%, 100% { transform:translateX(420%) skewX(-18deg); }
}
.login-card .btn.gold.block:hover { transform:translateY(-1px);
  box-shadow:inset 0 1px 0 #fffefa, inset 0 -1px 0 #7d5f2340,
    0 2px 4px #00000080, 0 18px 38px -12px #d9b45ba6; }
.login-card .btn.gold.block:active { transform:translateY(1px); }
.login-card .btn.gold.block:disabled { transform:none; }
.login-card .btn.gold.block:disabled::after { animation:none; opacity:0; }
/* `.login-err` was page-scoped before (the rule lived inside login.html), and
   index.html reuses the class on #sec-err with no rule of its own. Scoping it to
   .login-card keeps that exactly as it was rather than restyling index.html.
   It now sits directly under the field it belongs to, so the message and the thing
   it is about are read together — not a line at the bottom of the card. */
.login-card .login-err { color:#ff9a92; font-size:var(--t2); text-align:left; line-height:1.45;
  margin:var(--s2) 0 0; }
.login-card .login-err:empty { display:none; }
/* the retry after a dropped connection: the typed values are still in the fields,
   and this is the one tap that sends them again */
.login-card .btn.lg-retry { margin-top:var(--s2); min-height:48px; font-size:var(--t2);
  border-radius:14px; color:var(--gold2); border-color:#d9b45b66; background:#d9b45b0f; }
.login-card .btn.lg-retry:hover { background:#d9b45b1f; }
/* Same reasoning: `[hidden]` used to exist only on the login page. #otp-form is the
   only hidden element there, and it is inside .login-card — so this is a no-op
   everywhere else. (hidden already works by default; this beats a display: rule.) */
.login-card [hidden] { display:none !important; }
/* step two: the code — it fades up in place, no reload and no jump */
@keyframes nrStepIn { from { opacity:0; transform:translateY(10px) } to { opacity:1; transform:none } }
.login-card form:not([hidden]) { animation:nrStepIn var(--slow) var(--ease) both; }
.otp-kicker { text-align:center; color:var(--mut); font-size:var(--t1); font-weight:700;
  letter-spacing:.14em; text-transform:uppercase; margin:0 0 var(--s2); }
.login-card .otp-who { text-align:center; color:var(--gold2); font-size:var(--t3); font-weight:700;
  margin:0 0 var(--s3); }
.login-card .otp-hint { text-align:center; color:var(--mut); font-size:var(--t2);
  margin:0 0 var(--s4); line-height:1.5; }
.login-card .otp-input { text-align:center; letter-spacing:.36em; font-size:23px; font-weight:700;
  font-variant-numeric:tabular-nums; padding:var(--s3) var(--s2) var(--s3) var(--s4); min-height:56px; }
.login-card .otp-input.rec { letter-spacing:.12em; font-size:var(--t4); }
.login-card .otp-left { color:#c6d1e6; font-size:var(--t2); text-align:center;
  min-height:17px; margin-top:var(--s2); }
.login-card .otp-left.warn { color:#f0c98a; }
.login-card .otp-left.stop { color:#ff9a92; font-weight:700; }
.login-card .btn.ghost { margin-top:var(--s2); background:transparent; padding:12px;
  min-height:48px; font-size:var(--t2); border-radius:14px; }
.login-card .btn.ghost.back { color:#c6d1e6; border-color:#3b4c6d; }
/* the padlock line: what the connection is, said once, in the one place somebody
   about to type a password is looking */
.lg-trust { display:flex; align-items:center; justify-content:center; gap:7px;
  margin:var(--s5) 0 0; color:var(--mut); font-size:var(--t1); letter-spacing:.012em; }
.lg-trust svg { width:13px; height:13px; flex:none; color:var(--gold); }

/* ---- the arrival: three blocks, a beat apart ----
   The page already fades in as a whole (see `.login-wrap .welcome` in the motion
   section). These three give the blocks their order — masthead, then the way in,
   then the metal — which is the order they are meant to be read in. */
@media screen {
  .wl-brand   { animation:nrLiftIn 620ms var(--ease) 60ms both; }
  .login-card { animation:nrLiftIn 620ms var(--ease) 160ms both; }
  .wl-art     { animation:nrLiftIn 720ms var(--ease) 260ms both; }
}
@keyframes nrLiftIn {
  from { opacity:0; transform:translateY(24px) scale(.988); }
  to   { opacity:1; transform:none; }
}

/* ---- the desk: two columns, the form beside the brand and the metal ----
   980px is where 440px of card plus a readable measure of text stop fighting for the
   same row. Below it the single column above is the design, not a fallback. */
@media screen and (min-width:980px){
  .welcome { justify-items:stretch; gap:var(--s5) clamp(48px,5.6vw,88px);
    grid-template-columns:minmax(0,1.12fr) minmax(392px,462px);
    grid-template-areas:"brand form" "art form"; }
  .wl-brand, .wl-art { max-width:none; }
  .wl-brand { gap:var(--s4); padding-top:var(--s2); }
  .login-card { align-self:center; max-width:462px; }
  /* the metal lines up with the wordmark above it, not with the middle of the column */
  .wl-shot img { max-width:none; margin:0; }
}
/* ---- the tablet: one column still, but a column the size of the screen ---- */
@media screen and (min-width:560px) and (max-width:979px){
  .wl-brand, .wl-art, .login-card { max-width:600px; }
}
/* ---- the narrow end: the masthead tightens so the form still opens on screen ---- */
@media screen and (max-width:420px){
  .welcome { gap:var(--s5); }
  .login-card { padding:var(--s5) var(--s4) var(--s4); border-radius:22px; }
  .wl-brand { gap:var(--s2); }
  .wl-mark { width:72px; height:72px; }
  .login-logo { width:54px; height:54px; font-size:22px; }
  .wl-lead { font-size:var(--t2); line-height:1.55; }
  .wl-facts { gap:var(--s2); padding-top:var(--s2);
    grid-template-columns:repeat(3,minmax(0,1fr)); }
  .wl-facts li { padding:10px; }
  .wl-facts b { font-size:var(--t3); }
  /* the caption has to hold one line at 390px — "dirham settlement" is the longest
     string on the page that has nowhere to wrap to */
  .wl-facts span { font-size:var(--t1); letter-spacing:0; }
}
/* ---- a phone is a battery ----
   The motes and the rosette are the two effects that composite a layer every frame
   for the whole time the page is open, and they are the two a phone can least afford.
   Below 620px they are simply not there; the metal still floats, the light still
   walks across it and the rim still turns, which is the part anybody would miss. */
@media screen and (max-width:620px){
  .nr-dust { display:none; }
  .wl-shot::before { animation:none; }
  .login-card { -webkit-backdrop-filter:none; backdrop-filter:none; }
}

/* ============================================================================
   from admin.html — the desk console.
   The screen the owner keeps open all day, so it is built to be read at a glance
   rather than studied: the four numbers that answer "how is the desk", the accounts
   with the balance in the heaviest type in the row, the rules grouped with their
   units written next to them, and the log underneath. Everything here is colour,
   spacing and type from the seven values at the top of this file — no new palette.
   ============================================================================ */
/* The console is a desk tool, not a reading column: it carries nine columns of figures and
   six tools per row, and 1000px made the last of them wrap onto a line of its own. 1240px
   is the width the table needs to stay one glance; the customer's pages keep their own. */
.adm-wrap { max-width:1240px; margin:0 auto; padding:18px 14px 60px; }

/* ---- the summary strip: one line, four answers ---------------------------- */
.adm-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(178px,1fr));
  gap:var(--s3);margin:var(--s2) 0 var(--s5)}
/* the four answers the desk wants before it reads anything else: each one is a slab
   of the same family as the hero, lit from the same corner */
.kpi{position:relative;overflow:hidden;
  background:linear-gradient(170deg,#141d31,#0a0f1b 78%);border:1px solid #1e293e;
  border-radius:var(--r3);
  padding:var(--s4);display:flex;flex-direction:column;gap:var(--s1);min-width:0;
  box-shadow:var(--edge-soft), var(--sh1)}
.kpi::before{content:"";position:absolute;left:12%;right:12%;top:0;height:1px;
  background:linear-gradient(90deg,transparent,#d9b45b47,transparent)}
.kpi .k-l{font-size:var(--t1);text-transform:uppercase;letter-spacing:.13em;color:var(--mut);
  font-weight:600}
.kpi .k-v{font-size:var(--t4);font-weight:750;letter-spacing:-.028em;color:var(--txt);
  font-variant-numeric:tabular-nums;line-height:1.2}
.kpi .k-s{font-size:var(--t1);color:var(--mut);line-height:1.45}
/* "read 01:33" carries the clock: with proportional figures "01:33" fitted the phone tile on
   one line and "02:00" wrapped to two, so the console's height (and the deploy gate's
   phone-admin picture) depended on the time of day. Equal-width figures make it the same
   at every minute. */
#sum-price-state{font-variant-numeric:tabular-nums}
/* the fourth card is the one figure on the console that moves by itself, and the only
   one that is also a control — so it carries the metal the rest of the strip does not */
.kpi:last-child{background:
    radial-gradient(240px 120px at 84% -20%, #d9b45b1a, transparent 70%),
    linear-gradient(170deg,#1a1c18,#0b0d13 78%);
  border-color:#3a3524}
.kpi:last-child::before{background:linear-gradient(90deg,transparent,#d9b45b8c,transparent)}
/* the price is also the door to the price fields — a button, dressed as a figure */
.k-price{background:0 0;border:0;padding:0;text-align:left;cursor:pointer;color:var(--gold2);
  transition:color var(--fast) var(--ease),text-shadow var(--fast) var(--ease)}
.k-price:hover{color:var(--gold-lit);text-shadow:0 0 22px #d9b45b6b}
.k-price small{font-size:var(--t2);color:var(--mut);font-weight:600;margin-left:var(--s1)}
/* the sign-out button is alone on its side of the bar: a divider, not a shared row */
.top-sep{width:1px;height:26px;background:var(--line2);flex:none}
#svc-chip{cursor:default;font-weight:600}

/* ---- tools over the table: a search box and four filters ------------------ */
.adm-tools{display:flex;gap:var(--s3);align-items:center;flex-wrap:wrap;margin:0 0 var(--s3)}
.srch{flex:1 1 220px;min-width:0;background:var(--lift1);border:1px solid #28344d;
  color:var(--txt);
  border-radius:var(--r2);padding:var(--s2) var(--s3);font-size:var(--t3);font-family:inherit;
  min-height:42px;box-shadow:inset 0 2px 5px #00000066}
.srch::placeholder{color:#7382a0}
.srch:focus{border-color:#425574;outline:none}
.chips{display:flex;gap:var(--s1);flex-wrap:wrap}
.chip{background:transparent;border:1px solid var(--line2);color:var(--mut);
  border-radius:999px;padding:6px 12px;font-size:var(--t2);font-weight:600;cursor:pointer;
  min-height:36px;transition:background var(--fast) var(--ease),color var(--fast) var(--ease),
    border-color var(--fast) var(--ease)}
.chip:hover{border-color:#425574;color:var(--txt)}
.chip.on{background:#d9b45b1f;border-color:#b3923e;color:var(--gold2)}

/* ---- the failure bar: the server's own words, and a way to try again ------ */
.adm-alert{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  background:#2a1414;border:1px solid #ff7a7055;border-radius:var(--r2);
  padding:var(--s3) var(--s4);margin-bottom:var(--s3);color:#ffb0a8;font-size:var(--t2)}
.adm-alert[hidden]{display:none}
.adm-alert span{flex:1 1 220px;min-width:0;line-height:1.45}
.adm-hint{color:var(--mut);font-size:var(--t2);margin-top:var(--s3)}
.adm-hint[hidden]{display:none}

/* ---- the console's own second step: one bar at the head of the console, amber while
   this account has no two-factor, with the button that goes to the panel that turns it
   on. It is the console's copy of the statement-page strip, in the console's own
   language — the same words, one level louder, because this is the screen the desk
   sits in all day and the screen a stolen password would open. ---- */
.a2f-bar{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  background:linear-gradient(180deg,#342914,#231c10);border:1px solid #f0b35e6b;
  border-radius:var(--r2);
  padding:var(--s3) var(--s4);margin-bottom:var(--s3);color:#f5d19b;font-size:var(--t2);
  line-height:1.45;box-shadow:var(--edge-soft), var(--sh1)}
.a2f-bar[hidden]{display:none}
.a2f-bar .a2f-dot{width:9px;height:9px;border-radius:50%;background:currentColor;flex:none;
  animation:nrBlink 1.6s ease-in-out infinite}
.a2f-bar .a2f-txt{flex:1 1 260px;min-width:0}
.a2f-bar .btn{flex:none}

.adm-table { width:100%; border-collapse:collapse; }
.adm-table th, .adm-table td { border-bottom:1px solid #1e293e; padding:10px 8px; font-size:13px; text-align:left; }
.adm-table th { color:#98a2b5; font-weight:600; }
.adm-table td.num { text-align:right; font-variant-numeric:tabular-nums; }
/* Every figure in this table is right-aligned and tabular, so a column of balances can be
   read down and compared at a glance — the one thing the desk does with this table all day.
   The cell that holds them is nowrap: a figure broken over two lines is a misread waiting. */
.adm-table td.c-num { text-align:right; white-space:nowrap;
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1; }
/* a sortable heading says so, and says which way it is pointing */
.adm-table th[data-sort]{cursor:pointer;user-select:none;white-space:nowrap;
  transition:color var(--fast) var(--ease)}
.adm-table th[data-sort]:hover{color:var(--txt)}
.adm-table th[data-sort]::after{content:"↕";color:#4a5877;margin-left:6px;font-size:11px}
.adm-table th[aria-sort="ascending"]::after{content:"↑";color:var(--gold2)}
.adm-table th[aria-sort="descending"]::after{content:"↓";color:var(--gold2)}
/* the skeleton body: the shape of the table while the first answer is in flight */
.skel-body td{padding:14px 8px}
.skel-body .sk-bar{height:11px;margin:0}
/* one row = one customer. The balance is the heaviest figure in it, the open gold is
   the one that carries risk, and the last thing that happened sits under the name. */
.adm-table tbody#acct-body tr{transition:background var(--fast) var(--ease)}
.adm-table tbody#acct-body tr:hover{background:#141e35}
.adm-table .code{font-size:var(--t3);letter-spacing:.02em}
.adm-table .c-code{white-space:nowrap}
.adm-table .c-code .badge{margin-top:var(--s1)}
.adm-table .nm{font-weight:600;color:var(--txt)}
.row-sub{display:block;color:var(--mut);font-size:var(--t1);margin-top:2px;line-height:1.4;
  max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.num-lg{font-size:var(--t4);font-weight:800;letter-spacing:-.3px;color:var(--txt)}
.c-gold{color:#c6d1e6;font-weight:700}
.c-gold.zero{color:#5d6b85;font-weight:400}
tr.is-off{opacity:.72}
/* The tools get a column wide enough for two lines: the way in and the quiet three on the
   first, the pair that changes an account on the second. Narrower than this and every button
   takes its own line, which makes each row four times its height for no more information. */
.adm-table td.c-act{min-width:232px}
.adm-actions { display:flex; gap:5px; row-gap:7px; flex-wrap:wrap; align-items:center; }
.adm-actions button { font-size:12px; padding:5px 9px; }
/* the row's tools are in two groups: the way in (Open, gold), then the quieter ones.
   The dangerous pair sits behind a rule, so neither is one stray tap from Excel. */
.act-more{display:flex;gap:5px;flex-wrap:wrap}
.act-danger{display:flex;gap:5px;flex-wrap:wrap;padding-left:var(--s2);
  border-left:1px solid #3a2230}
.row-err{flex:1 1 100%;color:#ffb0a8;font-size:var(--t1);line-height:1.4;margin-top:2px}

.sec { margin-top:26px; }
.badge { padding:3px 9px; border-radius:20px; font-size:11px; font-weight:600;
  letter-spacing:.04em; box-shadow:inset 0 1px 0 #ffffff0a; }
.b-admin { background:#d9b45b22; color:#d9b45b; }
.b-cus { background:#3b82f622; color:#7cb8ff; }
.b-off { background:#ff5f5f22; color:#ff9a92; }
.b-2fa-on { background:#3ddc971f; color:#7ce3ae; }
.b-2fa-off { background:#98a2b51f; color:#98a2b5; }
.b-req { background:#f0b35e22; color:#f0c98a; }
.b-on { background:#37d1881f; color:#7ce3ae; }
/* the log's own labels */
.b-buy,.b-deposit,.b-gold_in{background:#37d1881f;color:#7ce3ae}
.b-sell,.b-withdraw,.b-gold_out{background:#d9b45b22;color:var(--gold2)}
.b-gold_margin,.b-maturity{background:#7cb8ff1a;color:#9cc7ff}
.b-security,.b-market,.b-order{background:#98a2b51f;color:#b2bbcc}
.b-danger{background:#ff5f5f22;color:#ff9a92}
.b-alert{background:#f0b35e22;color:#f0c98a}
.adm-actions .btn { min-height:34px; }
.btn.small.d,.btn.d.small{background:#3a1520;color:#ff9a92;border-color:#5c2430}
.btn.small.d:hover,.btn.d.small:hover{background:#4a1b28;border-color:#7a3040}
.btn.small.pri{min-height:34px;min-width:70px}
.adm-card { position:relative; background:linear-gradient(172deg,#111827,#0a0e19 72%);
  border:1px solid #1e293e; border-radius:14px; padding:20px;
  box-shadow:var(--edge-soft), var(--sh2); }
.adm-card::before{content:"";position:absolute;left:8%;right:8%;top:0;height:1px;
  background:linear-gradient(90deg,transparent,#d9b45b3d,transparent)}
/* (the console's cards take the same masthead — the rule for it is with the others) */
.adm-card .tbl-scroll { background:transparent; border:1px solid #1e293e; border-radius:10px; }
.frow2 { display:flex; gap:12px; flex-wrap:wrap; }
.frow2 > div { flex:1; min-width:140px; }
.frow2 label { display:block; font-size:12px; color:#b2bbcc; margin-bottom:5px; }
.frow2 input, .frow2 select { width:100%; box-sizing:border-box; background:#070910; border:1px solid #28344d; color:#f0f3fa; border-radius:8px; padding:10px 12px; font-size:14px; }

/* ---- market & rules: two groups, every field with its unit ---------------- */
.grp{border:1px solid var(--line);border-radius:var(--r3);padding:var(--s3) var(--s4) var(--s2);
  margin:0 0 var(--s3);min-width:0}
.grp legend{padding:0 var(--s2);color:var(--mut);font-size:var(--t1);font-weight:700;
  text-transform:uppercase;letter-spacing:.08em}
.grp .frow2 label{color:#c6d1e6;font-size:var(--t2);font-weight:600}
/* the unit is part of the label, so no field is a bare number — that reading cost the
   desk real money once, and this is the rule that stops it happening again. */
.unit{display:inline-block;background:#080b14;border:1px solid var(--line2);border-radius:999px;
  padding:1px 8px;margin-left:var(--s2);color:var(--mut);font-size:var(--t1);font-weight:600;
  white-space:nowrap}
.fnote{color:var(--mut);font-size:var(--t1);line-height:1.5;margin:var(--s2) 0 0;max-width:70ch}
.fhint{color:var(--mut);font-size:var(--t1);line-height:1.45;margin-top:var(--s1)}
.fld-err{color:#ffb0a8;font-size:var(--t2);line-height:1.45;margin-top:var(--s2)}
.fld-err[hidden]{display:none}
.frow .fld-err,.frow .fhint{margin-bottom:0}

/* the automatic-price switch: a real switch, and the warning beside it, not under it */
.auto-box{margin:var(--s2) 0 0;padding:var(--s3) var(--s4);border:1px solid var(--line2);
  border-radius:var(--r3);background:var(--lift2);box-shadow:var(--edge-soft)}
.switch{display:flex;gap:var(--s3);align-items:flex-start;cursor:pointer}
.switch input{position:absolute;opacity:0;width:0;height:0}
.sw-track{flex:none;width:46px;height:26px;border-radius:999px;background:#28344d;
  border:1px solid var(--line2);position:relative;transition:background var(--fast) var(--ease);
  margin-top:2px}
.sw-knob{position:absolute;top:2px;left:2px;width:20px;height:20px;border-radius:50%;
  background:#98a2b5;transition:transform var(--fast) var(--ease),background var(--fast) var(--ease)}
.switch input:checked + .sw-track{background:#1d5c3f;border-color:#37d18888}
.switch input:checked + .sw-track .sw-knob{transform:translateX(20px);background:#7ce3ae}
.switch input:focus-visible + .sw-track{outline:2px solid var(--gold2);outline-offset:3px}
.sw-txt{display:flex;flex-direction:column;gap:var(--s1);font-size:var(--t2);color:var(--txt)}
.sw-txt b{font-weight:700}
.sw-warn{color:#f0c98a;font-size:var(--t1);line-height:1.5;max-width:66ch}
.sw-warn b{color:#ffce8a}

/* the confirmation the save deserves: above the fields, with the time in it */
.save-bar{display:flex;align-items:center;gap:var(--s3);background:#0f1f19;
  border:1px solid #37d18855;border-radius:var(--r2);padding:var(--s3) var(--s4);
  margin-bottom:var(--s3);color:#7ce3ae;font-size:var(--t2);animation:nrRise var(--slow) var(--ease)}
.save-bar[hidden]{display:none}
.save-bar .sv-t{flex:1 1 auto;min-width:0}
.save-bar .x{color:#7ce3ae}
/* a field was touched and the save button has not been pressed since */
.mdirty{color:#f0c98a;font-size:var(--t1);font-weight:700;margin-left:var(--s3);
  padding:1px 8px;border:1px solid #f0b35e44;border-radius:999px;background:#f0b35e14}
.mdirty[hidden]{display:none}

/* ---- the log: one line per event, newest first --------------------------- */
.feed{list-style:none;margin:0;padding:0;border:1px solid #1e293e;border-radius:var(--r3);
  overflow:hidden;max-height:520px;overflow-y:auto;box-shadow:inset 0 1px 0 #ffffff08}
.fe-row{display:flex;gap:var(--s3);align-items:baseline;padding:var(--s2) var(--s4);
  border-bottom:1px solid var(--line);font-size:var(--t2);background:var(--lift3);
  transition:background var(--fast) var(--ease)}
.fe-row:hover{background:var(--lift5)}
.fe-row:last-child{border-bottom:0}
.fe-when{flex:none;color:var(--mut);font-variant-numeric:tabular-nums;font-size:var(--t1);
  width:104px}
.fe-who{flex:none;width:64px;font-weight:700;color:#c6d1e6}
.fe-type{flex:none}
.fe-detail{flex:1 1 260px;min-width:0;color:#c6d1e6;line-height:1.45;overflow-wrap:anywhere}
/* a reversal or a correction is the one thing in this list worth colouring: it means
   somebody had to put the books back the way they were. */
.fe-row.risk{background:#1c1119}
.fe-row.risk .fe-detail{color:#e3b6b0}
/* what was done from this console, in this session — not the server's log, and it says so */
.fe-row.mine{background:#121a2c}
.fe-row.mine .fe-who{color:var(--gold2)}
/* the two halves of the list, named: the session's own trail, and the accounts' record */
.fe-head{padding:var(--s2) var(--s4);background:#080b14;border-bottom:1px solid var(--line);
  color:var(--mut);font-size:var(--t1);font-weight:700;text-transform:uppercase;
  letter-spacing:.08em}
/* and the one line that says there is nothing to read — not an empty grey box */
.fe-row.none{color:var(--mut);justify-content:center;text-align:center;background:transparent}
.b-desk{background:#d9b45b1f;color:var(--gold2)}
/* the account's trading type in the accounts table — the same two colours as the chip on
   its statement (Physical: the desk's gold; Online: teal) */
.b-phys{background:#d9b45b1f;color:var(--gold2)}
.b-online{background:#2fb6a624;color:#6fe0cf}
/* Online card: a value the desk reads but does not set (the client's fixed margin, the
   unconfirmed fee) is printed, not offered as an input */
.ro-l{display:block;font-size:var(--t2);font-weight:600;color:#c6d1e6;margin-bottom:5px}
.ro-v{display:block;padding:10px 0;font-size:14px;color:var(--txt);font-weight:600}
.frow[hidden]{display:none}
.o-hist{margin-top:var(--s3);color:var(--mut);font-size:var(--t1)}
.o-hist summary{cursor:pointer;color:#c6d1e6;font-weight:600;min-height:24px}
.o-hist-list{margin:var(--s2) 0 0;padding-left:18px;line-height:1.6;overflow-wrap:anywhere}
.o-hist-list b{color:var(--txt)}

/* ---- new / edit account -------------------------------------------------- */
.pw-row{display:flex;gap:var(--s2)}
.pw-row input{flex:1 1 auto;min-width:0}
.pw-row .btn{flex:none}
.once{border:1px solid var(--line2);border-radius:var(--r3);padding:var(--s4);
  background:var(--lift2);animation:nrRise var(--slow) var(--ease);
  box-shadow:var(--edge-soft), var(--sh1)}
.once[hidden]{display:none}
.once-h{font-size:var(--t4);margin-bottom:var(--s2)}
.once-warn{color:#f0c98a;font-size:var(--t2);line-height:1.5;margin-bottom:var(--s3)}
.once-row{display:flex;align-items:center;gap:var(--s3);margin-bottom:var(--s2)}
.once-l{flex:none;width:78px;color:var(--mut);font-size:var(--t2)}
.once-row code{flex:1 1 auto;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:var(--t3);background:#070910;border:1px solid #28344d;border-radius:9px;
  padding:var(--s2) var(--s3);color:#e8cd7d;overflow-wrap:anywhere}

/* ---- the confirm dialog for a figure that moves every account ------------- */
#modal-confirm .modal{max-width:420px}
.cf-lead{color:#c6d1e6;font-size:var(--t2);line-height:1.5;margin-bottom:var(--s3)}
.cf-list{list-style:none;margin:0 0 var(--s3);padding:0}
.cf-list li{display:flex;gap:var(--s2);align-items:baseline;justify-content:space-between;
  padding:var(--s2) var(--s3);border:1px solid var(--line2);border-radius:var(--r2);
  margin-bottom:var(--s1);font-size:var(--t2);background:#090d17;
  font-variant-numeric:tabular-nums}
.cf-list .cf-name{color:#c6d1e6}
.cf-list .cf-figures{flex:none;white-space:nowrap}
.cf-list .cf-was{color:var(--mut)}
.cf-list .cf-arrow{color:var(--mut);padding:0 var(--s1)}
.cf-list .cf-now{color:var(--gold2);font-weight:700}

/* --- the last style="…" attributes in the markup, as classes --- */
/* These three carry !important on purpose: the originals were inline styles, which
   beat every sheet rule — including the print sheet's `.st td{padding:5px 5px}` and
   the phone block's `table.st.cards tbody td[colspan]{padding:14px 0}`. Dropping
   !important here would visibly change the empty rows in both of those modes. */
.hidden{display:none}
.empty-row{padding:20px !important;text-align:center;color:#98a2b5}
.empty-row.tight{padding:14px !important}
td.note-cell{max-width:220px !important}
.mt12{margin-top:12px}
.ml10{margin-left:10px}
.sec-col{flex:1;min-width:230px}
.sec-label{display:block;font-size:13px;color:#bcc8de;margin:16px 0 6px}
.sec-label.sm{font-size:12px;color:#b2bbcc;margin:0 0 6px}
.err-left{text-align:left}
.hl-gold{color:#f0c98a}
/* Generated by static/app.js — see the delegated click handler there. */
.mid.mid-block{width:100%;margin-bottom:8px;text-align:left;padding:13px}
.j-tx.j-none{color:#5d6b85}
.usd{font-size:13px}
.sub.cap-line{margin-top:5px}

/* ============================================================================
   15/09/2026 — the customer pass. Four things in one place, because they are the
   same idea: the screen should always say what it is doing. A skeleton while the
   first figures are in flight, a bar when the connection drops, a welcome line
   when a table is empty, one gold flash when a number changes — and the phone
   header arranged so the price and the balance are the first two things read.
   ============================================================================ */

/* ---- the period chip beside the account code ---- */
.hdr-period{font-size:var(--t1);color:var(--mut);border:1px solid #223055;
  border-radius:999px;padding:3px 10px;white-space:nowrap;background:#ffffff05;
  font-variant-numeric:tabular-nums;letter-spacing:.03em}

/* ---- 1. skeleton: the shape of the statement while the first paint is in flight ----
   #boot (the real statement) is not painted until /app.js says the first render has
   happened, so the page never shows an empty shell with a "—" where the balance goes
   and then shoves everything down a moment later. `is-ready` is added at the top of
   render(), so the two swap in the same frame. In PDF mode it is added immediately —
   a printed page must never contain a skeleton. */
.skel{max-width:1120px;margin:0 auto;padding:var(--s2) var(--s4) 70px}
body.is-ready .skel{display:none}
body:not(.is-ready) #boot{display:none}
/* the same slab as the hero, to the gradient — a skeleton that is a different shape
   from what replaces it makes the page appear to jump on first paint */
.skel-hero{border:1px solid #233152;border-radius:var(--r4);padding:var(--s5);
  margin:var(--s3) 0 var(--s5);box-shadow:var(--edge), var(--sh3);
  background:
    radial-gradient(760px 300px at 10% -45%, #d9b45b21, transparent 70%),
    radial-gradient(560px 300px at 96% 130%, #2a4d8f1f, transparent 70%),
    linear-gradient(168deg,#16203a 0%,#0e1524 46%,#0a0f1b 100%)}
.skel-paper{border:1px solid #1e293e;border-radius:var(--r3);padding:var(--s5) var(--s4);
  background:linear-gradient(170deg,#121a2c,#0a0e19 70%);box-shadow:var(--edge-soft)}
.sk-bar{height:12px;border-radius:6px;margin:0 0 var(--s3);
  background:linear-gradient(90deg,#182238 25%,#2c3c62 37%,#182238 63%);
  background-size:400% 100%;animation:nrShimmer 1.5s ease-in-out infinite}
.sk-bar:last-child{margin-bottom:0}
.skel-hero .sk-bar.big{height:42px;border-radius:var(--r2)}
.skel-head{display:flex;gap:var(--s4);flex-wrap:wrap;margin-bottom:var(--s5)}
.skel-head .sk-bar{margin:0}
.w14{width:14%}.w18{width:18%}.w22{width:22%}.w26{width:26%}.w34{width:34%}
.w44{width:44%}.w52{width:52%}.w62{width:62%}
.sk-row{display:flex;gap:var(--s4);margin-bottom:var(--s2)}
.sk-row .sk-bar{margin:0}
@keyframes nrShimmer{0%{background-position:100% 0}100%{background-position:0 0}}

/* ---- 2. the connection bar: what happened, what we are doing about it, and a way
   to stop waiting. It keeps the last figures on the page — a stale balance with a
   warning over it is worth more than an empty statement. ---- */
.netbar{position:sticky;top:0;z-index:60;display:flex;align-items:center;gap:var(--s3);
  max-width:1120px;margin:0 auto;padding:var(--s3) var(--s4);
  background:linear-gradient(180deg,#332814,#241d11);
  border-bottom:1px solid #f0b35e6b;color:#f5d19b;font-size:var(--t2);
  line-height:1.4;box-shadow:0 8px 24px -12px #000000b3}
.netbar[hidden]{display:none}
.netbar.off{background:#2a1414;border-bottom-color:#ff7a7066;color:#ffb0a8}
.netbar .nb-dot{width:9px;height:9px;border-radius:50%;background:currentColor;flex:none;
  animation:nrBlink 1.6s ease-in-out infinite}
.netbar .nb-txt{flex:1 1 auto;min-width:0}
.netbar .nb-btn{flex:none;background:#ffffff14;border:1px solid currentColor;color:inherit;
  border-radius:var(--r1);padding:var(--s2) var(--s3);font-size:var(--t2);font-weight:600;
  cursor:pointer;min-height:40px;
  transition:background var(--fast) var(--ease)}
.netbar .nb-btn:hover{background:#ffffff26}
@keyframes nrBlink{0%,100%{opacity:1}50%{opacity:.35}}

/* ---- 3. empty states: a table with nothing in it should say so, and say what to
   do next. Same markup slot as before (a full-width cell), one level tidier. ---- */
/* white-space is reset here on purpose: the statement's `.st td{white-space:nowrap}` is
   inherited, and an empty state lives inside one of those cells — without this its
   sentence runs off the edge of the sheet in a single unbreakable line. */
.empty-state{display:flex;flex-direction:column;align-items:center;gap:var(--s2);
  padding:var(--s5) var(--s4);text-align:center;white-space:normal}
.empty-state .es-ic{width:46px;height:46px;border-radius:50%;display:grid;place-items:center;
  border:1px solid var(--paper-line);background:#fffdf5;color:#8a6a1f;font-size:22px;
  font-family:var(--display);font-weight:400;
  box-shadow:0 0 0 4px #d9b45b14, inset 0 1px 0 #fff}
.empty-state h3{font-size:var(--t3);font-weight:700;color:var(--paper-txt);line-height:1.3}
.empty-state p{font-size:var(--t2);color:var(--paper-mut);max-width:46ch;line-height:1.5}
.empty-state .btn{margin-top:var(--s2)}
/* the same block on the dark panels (orders, results) */
.empty-state.dark .es-ic{background:#0b101c;border-color:var(--line2);color:var(--gold)}
.empty-state.dark h3{color:var(--txt)}
.empty-state.dark p{color:var(--mut)}

/* ---- 4. one gentle gold flash when a figure changes on a refresh ----
   A single pass, no repetition, no motion: the number is already in place, this only
   says "this one moved". Wired in app.js (see flashChanged). */
@keyframes nrFlash{0%{color:var(--gold2)}100%{color:inherit}}
.nr-flash{animation:nrFlash 640ms var(--ease) 1}

/* ---- the first frame ----------------------------------------------------
   The statement is not painted until app.js has the figures (body.is-ready), so the
   moment it appears it appears whole. One 260ms fade with four pixels of travel is
   the difference between a page that switches on and a page that arrives; it runs
   once, never on a refresh of the figures, and the reduced-motion block below turns
   it off entirely for anyone who asked the system for calm. */
@keyframes nrArrive{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
/* SCREEN ONLY, and not as a matter of taste: `#boot` is `body>.wrap`, which is the one
   element the print stylesheet keeps — so an animation on it is an animation on the
   customer's PDF. A filling animation leaves the element composited even after it has
   settled, and Chrome's PDF writer rasterises a composited subtree instead of emitting
   it as vector text: the same six pages came out 53 KB heavier, with 34 more streams in
   them, for a fade nobody printing a statement will ever see. */
@media screen{
  body.is-ready #boot{animation:nrArrive 260ms var(--ease) both}
  .login-wrap .welcome{animation:nrArrive 320ms var(--ease) both}
}

/* ---- 5. the statement's own motion: 120–200ms, and nothing that moves far ---- */
@keyframes nrFade{from{opacity:0}to{opacity:1}}
@keyframes nrRise{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.toast{transition:opacity var(--slow) var(--ease), transform var(--slow) var(--ease)}
.st tbody tr{transition:background var(--fast) var(--ease)}
.mid,button.big,.btn{transition-duration:var(--fast)}

/* ---- 6. reduced motion: everything above becomes instant, and the live dot stops
   pulsing. A customer who asked the system for calm gets calm. ---- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;scroll-behavior:auto !important}
}

/* ---- 7. phone: the header is two rows, not three crowded ones ----
   Row one is who this is (name + account code); row two is the price with its honest
   live state, and the two tools share that row. The balance follows immediately, so it
   is visible without scrolling on any phone.
   The two pills are named through `.brand` on purpose: `.brand span{display:block}` is a
   heavier selector than `.hdr-period`, so a bare rule for it never won — that is exactly
   how the period chip stayed on screen and how the two pills stacked into a third row. */
@media screen and (max-width:620px){
  .appbar{padding:var(--s3) var(--s3) var(--s2);gap:var(--s2);align-items:center}
  .brand{flex:1 1 100%;order:1;gap:var(--s2);min-width:0}
  /* min-width:0 lets the name block shrink, so the ellipsis below can work: without it a
     long name ("Mariam Khalid Rashid (MK05)") pushed a 320px page 24px sideways */
  .brand>div{min-width:0}
  .brand h1{font-size:var(--t3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .brand span{font-size:var(--t1)}
  .brand .acct-chip{display:inline-flex;align-items:center;flex:none;
    font-size:var(--t1);padding:3px 9px}
  .brand .hdr-period{display:none}
  /* the mark is a struck medallion at every width — it was the one element that
     turned back into a rounded square on a phone */
  .logo{width:38px;height:38px;font-size:17px;border-radius:50%}
  .mkbar{order:2;flex:1 1 auto;gap:var(--s2);flex-wrap:nowrap;min-width:0;
    padding:3px var(--s3)}
  /* the price is a button on the strip (for the desk it opens the price dialog, for a
     customer it explains who sets the price) — 44px tall so a thumb can hit it */
  .mkbar-price{font-size:var(--t3);min-height:44px;display:inline-flex;align-items:center}
  .mkbar-book,.mk-sync{display:none}
  .mk-state{padding:2px var(--s2);font-size:var(--t1);flex:none}
  .top-right{order:3;flex:0 0 auto;gap:var(--s2);flex-wrap:nowrap}
  .top-right .btn{min-height:44px;padding:var(--s2) 10px;font-size:var(--t2);
    display:inline-flex;align-items:center}
  .hero{padding:var(--s4) var(--s3);gap:var(--s3);margin-top:var(--s2)}
  .hero-equity{font-size:clamp(28px,9vw,38px);letter-spacing:-.5px}
  .hero-equity small{font-size:var(--t3)}
  .wrap{padding:var(--s2) var(--s3) 70px}
  .skel{padding:var(--s2) var(--s3) 70px}
  .netbar{padding:var(--s2) var(--s3);gap:var(--s2);font-size:var(--t1)}
  .empty-state{padding:var(--s4) var(--s2)}
  .app-foot{padding:var(--s3) var(--s3) var(--s5)}
  .st tr.day-band{display:none}
}
/* ---- the second row has to stay ONE row -------------------------------------
   Inter sets a wider figure than the system face this header was measured against,
   and the difference was enough to push the two tools onto a third row at 390px
   (measured: 197px of ticker + 166px of tools in 366px of bar). The unit therefore
   leaves the price line at 430px instead of 360px — it is still in the button's
   title, and the sheet below prints "per oz" in full — and the tools tighten by a
   few pixels. Two rows again at every width from 320 up. */
@media screen and (max-width:430px){
  .mkbar-price small{display:none}
  .mk-state{padding:2px 7px}
  .top-right{gap:6px}
  .top-right .btn{padding:var(--s2) 9px}
}

/* The narrowest phones (320–360): row two has to hold the price, the state and both
   tools in ~300px. The unit goes off the price line — it is in the button's title, and
   the sheet below prints "per oz" in full anyway — and the tools tighten up. */
@media screen and (max-width:360px){
  .hero-meta,.hero-avail,.hero-cap,.hero-note{font-size:var(--t1)}
  .mkbar-price small{display:none}
  .top-right .btn{padding:var(--s2) 6px;font-size:var(--t1)}
}

/* ---- phone: the console stops being a wide table ------------------------------
   Same markup and the same figures as the desk sees — below 760px each account is its
   own block, with the column's own name printed beside the number by the stylesheet
   (`data-l`), and the tools laid out for a thumb: Open full width at the top of the
   group, the dangerous pair last and on their own line. The table used to live inside
   a horizontal scroller, which on a phone meant seeing four columns and guessing at the
   rest, with the buttons four screens to the right. */
@media screen and (max-width:760px){
  .adm-wrap{padding:var(--s3) var(--s3) 70px}
  .adm-strip{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--s2)}
  .kpi{padding:var(--s3)}
  .kpi .k-v{font-size:var(--t3)}
  .k-price{font-size:var(--t4)}
  .adm-card{padding:var(--s4) var(--s3)}
  .adm-card .tbl-scroll{border:0;background:none;overflow:visible}
  .adm-scroll{overflow-x:visible}
  /* the columns' names move beside each figure (data-l), so the header row goes — without
     this the header cells keep their own table box and drag the page sideways */
  .adm-table thead{display:none}
  .adm-table,.adm-table tbody,.adm-table tr,.adm-table td{display:block;width:100%}
  .adm-table tr{border:1px solid var(--line);border-radius:var(--r3);
    padding:var(--s3) var(--s4);margin-bottom:var(--s2);background:#0b101c}
  .adm-table tbody#acct-body tr:hover{background:#0b101c}
  .adm-table td{border:0;padding:2px 0;display:flex;gap:var(--s3);align-items:baseline;
    justify-content:space-between;text-align:right;font-size:var(--t2)}
  .adm-table td::before{content:attr(data-l);flex:none;text-align:left;color:var(--mut);
    font-size:var(--t1);text-transform:uppercase;letter-spacing:.06em;font-weight:600}
  .adm-table td.c-code{justify-content:flex-start;align-items:center;gap:var(--s2);
    padding-bottom:var(--s1);font-size:var(--t3)}
  .adm-table td.c-code::before,.adm-table td.c-name::before{content:none}
  .adm-table td.c-name{display:block;text-align:left;padding-bottom:var(--s2);
    border-bottom:1px solid var(--line);margin-bottom:var(--s2)}
  .adm-table td.c-bal{padding:var(--s2) 0}
  .adm-table td.c-bal .num-lg{font-size:var(--t4)}
  .row-sub{max-width:none;white-space:normal}
  .adm-table td.c-act{display:block;padding-top:var(--s3);margin-top:var(--s2);
    border-top:1px solid var(--line)}
  .adm-actions{display:block}
  .adm-actions .btn.gold.small{display:block;width:100%;min-height:46px;font-size:var(--t3);
    margin-bottom:var(--s2)}
  .act-more,.act-danger{display:grid;grid-template-columns:1fr 1fr;gap:var(--s2);
    padding-left:0;border-left:0;margin-bottom:var(--s2)}
  .act-danger{padding-top:var(--s2);border-top:1px dashed #3a2230}
  .act-more .btn,.act-danger .btn{min-height:42px;justify-content:center;font-size:var(--t2)}
  /* the log wraps: when, who and what on the first line, the sentence under it. It keeps a
     ceiling of its own on a phone as well — a hundred and fifty events unrolled make a page
     nobody can scroll to the end of. */
  .feed{max-height:66vh}
  .fe-row{flex-wrap:wrap;gap:var(--s2);padding:var(--s3)}
  .fe-when{width:auto}
  .fe-detail{flex:1 1 100%}
  .adm-tools{gap:var(--s2)}
  .srch{flex:1 1 100%}
  .chip{flex:1 1 auto;text-align:center}
  .frow2 > div{min-width:120px}
  .pw-row{flex-wrap:wrap}
  .once-row{flex-direction:column;align-items:stretch;gap:var(--s1)}
  .once-l{width:auto}
}

/* ============================================================================
   THE STATEMENT BECOMES ONE OBJECT WITH THE SITE          17/09/2026
   ============================================================================
   Until now this page was two products stacked on top of each other: an obsidian
   application, then five thousand pixels of cream paper, then an obsidian panel
   again. The sheet is 75% of the page's height, so "the design" of this site was,
   in practice, a light document with a dark frame around it — and the seam where
   the two met was the loudest thing on the screen.

   The sheet is now the same material as everything else: a dark document, a step
   lifted off the page, ruled in the desk's gold. It still reads as a DOCUMENT and
   not as another panel — it keeps its letterhead, its numbered sections, its
   ruled tables, its serif masthead and its generous margins. What it stops doing
   is pretending to be a piece of paper on a screen that is not one.

   WHAT THIS BLOCK IS ALLOWED TO TOUCH
   Everything here is inside `@media screen`, and that is the whole safety
   argument. The base rules above — the ones the print stylesheet builds on — are
   untouched, and `--paper`, `--paper-txt`, `--paper-mut` and `--paper-line` keep
   their cream values everywhere except a screen. So:

       * the PDF a customer downloads is byte-for-byte the document it was
       * Ctrl+P gives the desk the same sheet it always gave
       * the Excel workbook never saw any of this

   Four variables do most of the work (every rule written against them flips for
   free); what follows them is the handful of places the old sheet hardcoded a
   cream, a stone or an ink, which have to be named one at a time.
   ============================================================================ */
@media screen{

  :root{
    /* the sheet, on a screen: one step lifted off the page, cool rather than warm,
       so the gold rules on it are the only warm thing in the document */
    --paper:#0b1017;
    --paper-txt:#e9eef8;
    --paper-mut:#93a2bc;
    --paper-line:#1e2839;
    /* the document's own furniture, used only inside this block */
    --doc-band:#141b29;      /* a table header, a day band                */
    --doc-zebra:#0d131d;     /* the banded row                            */
    --doc-hover:#151d2c;     /* the row under the cursor                  */
    --doc-rule:#1a2333;      /* the hairline between two rows             */
    --doc-chip:#131b28;      /* a control that lives on the sheet         */
  }

  /* ---- the sheet itself -------------------------------------------------
     A lifted surface, not a panel: a hairline of gold around it, the engraving
     pressed into its top-right corner, and a shadow deep enough to say it is
     lying ON the page rather than cut out of it. */
  /* THE SHEET IS PAINTED FLAT, AND ITS ORNAMENT IS A SMALL BOX
     The first cut of this put the engraving and a long gradient into the sheet's own
     `background`. That is correct CSS and a genuine performance bug: this element is
     seven thousand pixels tall on a desk and twenty-five thousand on a phone, and a
     background belongs to the whole box — so every scroll asked the compositor to
     re-rasterise a twelve-pass vector rosette and a 7,000px gradient. Measured in
     WebKit on 17/09/2026: it delayed the frame that repaints the section strip enough
     that the chip lagged a whole section behind the one on screen, which
     scripts/e2e_browser.py caught and which a customer would have felt as a strip that
     could not keep up with their thumb.

     So the sheet is one flat colour, and the ornament is a 340px box in the corner
     that carries the engraving and the top light together. `isolation:isolate` gives
     the sheet a stacking context of its own, which is what lets the ornament sit at
     `z-index:-1` — above the sheet's background, UNDER every figure printed on it,
     so nothing is ever read through it. It costs no layout, no scroll container (the
     wide tables' sticky headers keep working) and one small raster. */
  .paper{
    background:var(--paper);
    isolation:isolate;
    border:1px solid #223050;
    box-shadow:inset 0 1px 0 #ffffff12, 0 2px 8px #00000080,
      0 36px 90px -28px #000000e6;
    padding:30px 32px 22px;
  }
  /* Two layers, because they are held at two very different strengths and a single
     `background` has one opacity for all of its layers. ::before is the light falling
     on the head of the sheet; ::after is the engraving, at six percent — the strength
     it was always meant to be, and the strength it had while it was still a background
     under a wash of the paper's own colour. Both sit at z-index:-1 inside the sheet's
     stacking context: above its background, under every figure printed on it. */
  .paper::before{content:"";position:absolute;z-index:-1;pointer-events:none;
    left:0;right:0;top:0;height:360px;border-radius:17px 17px 0 0;
    background:linear-gradient(180deg,#131c2e,#0b101700)}
  .paper::after{content:"";position:absolute;z-index:-1;pointer-events:none;
    right:0;top:0;width:330px;height:330px;border-radius:0 17px 0 0;opacity:.06;
    background:url(/guilloche.svg) right -196px top -210px/540px 540px no-repeat}

  /* ---- the letterhead ----------------------------------------------------
     Two rules under it, the way an engraved letterhead is printed in two passes:
     a heavy one in the document's own ink and a hairline of the desk's gold. */
  .paper-head{border-bottom:1px solid #2b3a58;padding-bottom:14px}
  .paper-head::after{background:linear-gradient(90deg,#d9b45b,#d9b45b66 52%,transparent)}
  .doc-desk-sub{color:var(--paper-mut)}
  .est{color:var(--gold2)}
  .est-sub{color:var(--paper-mut)}

  /* ---- the identity block and the summary tiles -------------------------- */
  .meta label{letter-spacing:.14em;font-weight:600}
  .meta b{color:var(--paper-txt)}
  .ov-grid{border-color:#22304a;border-radius:12px;background:#080d15}
  .ov{background:linear-gradient(180deg,#111827,#0b1119);
    border-right-color:var(--doc-rule);border-bottom-color:var(--doc-rule)}
  .ov label{letter-spacing:.11em;font-weight:600;color:var(--paper-mut)}
  .ov b{color:var(--paper-txt)}

  /* ---- the numbered sections --------------------------------------------
     The numeral is the one gold mark in the body of the document, and the rule
     under each heading fades out to the right instead of stopping dead. */
  .sec-title{border-bottom:0;position:relative;padding-bottom:9px;color:#aab8d1}
  .sec-title::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
    background:linear-gradient(90deg,#d9b45b59,#22304a 34%,transparent)}
  .sec-n{color:var(--gold);font-weight:700}

  /* ---- the tables --------------------------------------------------------
     A ruled table, not a grid of boxes: one band at the head, a hairline between
     rows, a banded even row a shade off the sheet, and a row that lights up under
     the cursor. Every figure in it keeps the tabular alignment it had. */
  .tbl-scroll{background:#080d15;border-color:#22304a;border-radius:12px}
  .st th{background:linear-gradient(180deg,#182131,#111826);color:#aab8d1;
    border-bottom:1px solid #2a3a59}
  .st thead tr.hgrp th{background:#1b2536;color:#b9c6de;
    border-right-color:#2a3a59;border-bottom-color:#2a3a59}
  .st td{border-bottom:1px solid var(--doc-rule)}
  .st tr:nth-child(even) td{background:var(--doc-zebra)}
  .st tbody tr:hover td{background:var(--doc-hover)}
  .st tr.day-band td{background:var(--doc-band);border-top:1px solid #26334d;
    border-bottom:1px solid #22304a;color:#b3c1da}
  .st tr.day-band td:hover,.st tr.day-band:hover td{background:var(--doc-band)}
  .st tr.day-band td.band-sum{color:#cbd6ea}
  .st .mu{color:var(--paper-mut)}

  /* A profit and a loss go back to the two colours the rest of the site uses for
     the sign of a number. The darkened pair above them (#15724a / #a92c25) exists
     because those two are unreadable on cream — they are, and they stay, the
     PRINTED sheet's colours. On this surface they measure 9.2:1 and 7.1:1, which
     scripts/e2e_a11y.py prints on every run. */
  .paper .up,.paper td.up{color:var(--up)}
  .paper .down,.paper td.down{color:var(--down)}

  /* a row that was reversed, and the opening line the book starts from */
  tr.muted-row{background:#0e1018 !important}
  tr.muted-row td{color:#7b8698}
  tr.muted-row td b{color:#6c778a}
  tr.opening-row td{background:#1a1810 !important;color:#e7dcc0}

  .total-row{color:var(--paper-mut)}
  .total-row b{color:var(--paper-txt)}

  /* ---- the controls that live on the sheet ------------------------------
     The row actions (Sell, Reminder, Correct) and the three document buttons.
     They are the sheet's own furniture, so they are cut from the sheet's own
     material rather than borrowed off the dark panels. */
  .mini button{border:1px solid #2b3a58;background:var(--doc-chip);color:#cdd8ec}
  .mini button:hover{background:#1b2537;border-color:#3d5075}
  .mini button.d{color:#ff9a92;border-color:#5a2b2b}
  .mini button.d:hover{background:#2a1520;border-color:#7a3040}
  .mini button.armed{background:#2a2113;border-color:var(--gold);color:var(--gold2)}
  .mini button.hit{background:#12251c;border-color:#3ddc9766;color:#7ce3ae}
  .paper-print{border:1px solid #2b3a58;background:var(--doc-chip);color:#cdd8ec}
  .paper-print:hover{background:#1b2537}
  .paper .btn.ghost,.paper-actions .btn.ghost{
    background:linear-gradient(180deg,#1a2338,#131a2b);color:#dbe4f5;
    border:1px solid #2b3a58;box-shadow:var(--edge-soft),var(--sh1)}
  .paper .btn.ghost:hover{background:linear-gradient(180deg,#222d47,#182137);
    border-color:#425574}
  .paper .btn.gold{box-shadow:inset 0 1px 0 #fffdf4b3, inset 0 -1px 0 #7d5f2340,
    0 1px 2px #00000073, 0 10px 24px -10px #d9b45b6b}

  /* ---- the ledger's own filter bar --------------------------------------- */
  .filter-bar{border:1px solid #22304a;background:#080d15}
  .filter-bar label{color:var(--paper-mut)}
  .filter-bar input,.filter-bar select{background:#05080f;border:1px solid #2b3a58;
    color:var(--paper-txt);box-shadow:inset 0 2px 5px #00000066}
  .filter-bar input:focus,.filter-bar select:focus{border-color:var(--gold);
    outline:0;box-shadow:inset 0 2px 5px #00000066, 0 0 0 3px #d9b45b26}

  /* ---- nothing here yet -------------------------------------------------- */
  .empty-state .es-ic{background:#111827;border-color:#2b3a58;color:var(--gold);
    box-shadow:0 0 0 4px #d9b45b12, inset 0 1px 0 #ffffff14}
  .empty-state h3{color:var(--paper-txt)}
  .empty-state p{color:var(--paper-mut)}

  /* ---- the phone's card layout is the same material ---------------------- */
  @media (max-width:760px){
    table.st.cards tbody tr{background:linear-gradient(180deg,#111827,#0b1119);
      border-color:#22304a;box-shadow:var(--edge-soft)}
    table.st.cards tbody tr.voided-row{background:#140f14;border-color:#3a2530}
    table.st.cards tbody tr.opening-row{background:#1a1810;border-color:#43391f}
    table.st.cards tbody td::before{color:var(--paper-mut)}
  }
}

/* ============================================================================
   THE ROOM, EVERYWHERE                                            17/09/2026
   ============================================================================
   The sign-in page was lit first — a drifting aurora, motes in the air, metal that
   floats, a card of smoked glass with a rim of the alloy turning round it. This
   section carries the same material to the two pages behind the password, because a
   customer who signs in through that door and lands on a flat black spreadsheet has
   been shown two different companies.

   IT IS CALIBRATED DOWN, and deliberately. The welcome page is a landing: it can
   afford a bloom that breathes and fourteen motes. The statement is a WORKING PAGE
   with a man's money on it, so here the same ideas run slower, quieter, and never on
   top of a figure:

     · the light drifts over 46 seconds instead of 34, on a layer of its own
     · the motes are ten, not fourteen, and at half the brightness
     · the balance panel's rim turns once every 22 seconds, at two thirds the alpha
     · nothing animates over a number — the glow breathes BEHIND the panel, the
       sweep walks across a BUTTON, and the tables are left alone entirely

   THREE THINGS THIS SECTION MAY NOT DO, and does not:
     · print. Everything below is inside `@media screen`, so the sheet headless
       Chrome fetches for the PDF is the document it was before, to the byte.
     · move for anyone who asked for calm. The global `prefers-reduced-motion` block
       carries `!important`, so it wins over every rule here whatever the order.
     · cost a phone its battery. The heaviest layers switch off under 620px.
   ============================================================================ */
@media screen {

/* ---- 1. the light in the room, drifting -----------------------------------
   `body::before` carries the room's four static layers — the two big lights, the
   guilloché raster and the film grain — and it MUST stay still: moving it would slide
   the grain and the engraving across the page, which reads as the texture coming
   loose. So the drift goes on a layer of its own, with nothing on it but light.
   Not on the sign-in page: that one has its own aurora, tuned to its own composition. */
body:not(.login-page)::after { content:""; position:fixed; inset:-16%; z-index:-1;
  pointer-events:none;
  background:
    radial-gradient(900px 520px at 78% 6%, #d9b45b17, transparent 62%),
    radial-gradient(780px 480px at 6% 94%, #24365c30, transparent 70%);
  animation:nrDrift 46s ease-in-out infinite; }
@keyframes nrDrift {
  0%,100% { transform:translate3d(0,0,0) scale(1); }
  33%     { transform:translate3d(1.6%,-1.2%,0) scale(1.06); }
  66%     { transform:translate3d(-1.4%,1.1%,0) scale(1.03); }
}

/* ---- 2. the motes ---------------------------------------------------------
   The same specks as the welcome page, at two thirds the count and rather dimmer, on
   a FIXED layer so they are always in the air and never scroll away down a seven-
   thousand-pixel statement. `.nr-dust` keeps the geometry it has on the sign-in page;
   this is the modifier the other two pages carry. */
.nr-dust.fixed { position:fixed; z-index:-1; }
.nr-dust.fixed i { animation-name:nrDustQuiet; }
@keyframes nrDustQuiet {
  0%   { opacity:0; transform:translate3d(0,0,0) scale(.55); }
  12%  { opacity:.42; }
  70%  { opacity:.24; }
  100% { opacity:0; transform:translate3d(24px,-108vh,0) scale(1.1); }
}

/* ---- 3. the mark, struck ---------------------------------------------------
   The monogram in the masthead becomes the same hallmark the welcome page strikes: a
   ring of knurl round the medallion, turning once every twenty-six seconds. It is the
   logo's own ::after, so neither page gained an element for it. */
.logo { position:relative; }
.logo::after { content:""; position:absolute; inset:-6px; border-radius:50%;
  border:1px dashed #d9b45b3d; pointer-events:none;
  animation:nrTurn 26s linear infinite; }

/* ---- 4. the balance panel: glass, and a rim of the alloy --------------------
   The hero carries the figure the whole page exists for, so it gets the sign-in
   card's treatment: a slightly transparent fill with the room blurred behind it, and
   the alloy running round the edge. `.hero` is `overflow:hidden`, so the rim is drawn
   one pixel INSIDE the edge rather than on `inset:-1px` the way the card's is. */
.hero { -webkit-backdrop-filter:blur(14px) saturate(125%);
  backdrop-filter:blur(14px) saturate(125%);
  background:
    radial-gradient(760px 300px at 10% -45%, #d9b45b21, transparent 70%),
    radial-gradient(560px 300px at 96% 130%, #2a4d8f1f, transparent 70%),
    linear-gradient(168deg,#16203af2 0%,#0e1524f7 46%,#0a0f1bfa 100%);
  box-shadow:var(--edge), var(--sh3), 0 0 64px -34px #d9b45b80; }
.hero-rim { position:absolute; inset:0; border-radius:inherit; padding:1px;
  pointer-events:none; z-index:0;
  background:conic-gradient(from var(--nr-spin),
    #d9b45b00 0deg, #d9b45b00 46deg, #f0d48ab3 86deg, #fffaec 104deg, #f0d48ab3 122deg,
    #d9b45b00 168deg, #d9b45b00 230deg, #f0d48a99 268deg, #fdf3d6e6 284deg,
    #f0d48a99 300deg, #d9b45b00 340deg, #d9b45b00 360deg);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite:exclude;
  animation:nrRim 22s linear infinite; }
/* the engraved corner breathes — it is the only thing on this panel that moves, and
   it is five percent of an ornament in the corner furthest from every figure */
.hero::after { animation:nrPlate 9s ease-in-out infinite; }
@keyframes nrPlate {
  0%,100% { opacity:.05; transform:rotate(0deg) scale(1); }
  50%     { opacity:.085; transform:rotate(3deg) scale(1.03); }
}
/* the figure itself is never animated: what breathes is the warmth in its shadow */
.hero-equity { animation:nrEquityGlow 9s ease-in-out infinite; }
@keyframes nrEquityGlow {
  0%,100% { text-shadow:0 1px 0 #00000059, 0 10px 40px #d9b45b1f; }
  50%     { text-shadow:0 1px 0 #00000059, 0 12px 54px #d9b45b40; }
}

/* ---- 5. every raised surface answers the pointer ---------------------------
   One rule, one idea: a panel, a tile, a card and a chip all lift a little and take
   the alloy on their rim when the pointer is over them. It is what turns a page of
   printed rectangles into a page of things. Nothing moves more than two pixels and
   nothing moves for longer than 180ms. */
.panel, .stat, .chart-card, .adm-card, .sec-card, .mt, .snav, .mkbar {
  transition:border-color var(--slow) var(--ease), transform var(--slow) var(--ease),
    box-shadow var(--slow) var(--ease), background var(--slow) var(--ease); }
.stat:hover, .chart-card:hover, .mt:hover {
  transform:translateY(-2px); border-color:#3a4c72;
  box-shadow:var(--edge), 0 12px 26px -16px #000000cc, 0 0 24px -14px #d9b45b80; }
.panel:hover, .adm-card:hover {
  border-color:#2b3c5e; box-shadow:var(--edge), var(--sh3), 0 0 46px -30px #d9b45b73; }
.snav:hover { transform:translateY(-1px); }
.mkbar:hover { border-color:#2c3c5e;
  box-shadow:inset 0 1px 0 #ffffff0f, inset 0 -8px 20px -14px #d9b45b66; }
/* a row of the book under the pointer takes a hairline of the alloy down its leading
   edge. An INSET shadow, so no cell moves and no column shifts by a pixel. */
.st tbody tr:hover { box-shadow:inset 3px 0 0 -1px #d9b45b80; }

/* ---- 6. the gold button, struck and lit ------------------------------------
   The sweep the sign-in button carries, given to the rest of them. On an ordinary
   gold button it runs ONCE, on hover — a page with sixteen of them blinking at each
   other is a fairground. On the two that are the point of their page it runs on its
   own clock. Always a pseudo-element, never a child: several of these buttons have
   their whole text content replaced while they wait on the server. */
.btn.gold { position:relative; overflow:hidden; }
.btn.gold::after { content:""; position:absolute; top:-60%; bottom:-60%; left:0;
  width:36%; pointer-events:none; opacity:0;
  background:linear-gradient(90deg,#ffffff00,#ffffffb3,#ffffff00);
  transform:translateX(-160%) skewX(-18deg); }
.btn.gold:hover::after { opacity:1; animation:nrSwipe 760ms var(--ease) 1; }
@keyframes nrSwipe {
  from { transform:translateX(-160%) skewX(-18deg); }
  to   { transform:translateX(420%)  skewX(-18deg); }
}
.btn.gold:hover { transform:translateY(-1px); }
.btn.gold:active { transform:translateY(1px); }
.btn.gold:disabled::after { animation:none; opacity:0; }
/* the printed sheet is paper, and paper does not catch a specular highlight */
.paper .btn.gold::after, .paper-actions .btn.gold::after { display:none; }
/* the sign-in button keeps the sweep it was given with the welcome page: its rule is
   written earlier in this sheet and does not name `opacity`, so without this line the
   `opacity:0` above would reach it and put its light out. */
.login-card .btn.gold.block::after { opacity:1; }
/* Buy and Sell already carry one specular band lying across the face (button.big
   ::after). It was a highlight that did not move; now it walks, once every five and
   a half seconds, and Sell is held two and a half seconds out of phase so the pair
   never flash together. Nothing else about either button changed. */
button.big::after { background-size:230% 100%; background-repeat:no-repeat;
  animation:nrFace 5.4s ease-in-out infinite; }
/* `button.big.sell::after` re-declares the `background` SHORTHAND at a higher
   specificity than the rule above, and a shorthand resets background-size to auto —
   so the green one has to be handed the size back or it never sweeps at all. */
button.big.sell::after { animation-delay:2.6s; background-size:230% 100%; }
@keyframes nrFace {
  0%, 58%   { background-position:128% 0; }
  88%, 100% { background-position:-38% 0; }
}
button.big:disabled::after { animation:none; }

/* ---- 7. the ruled line under the masthead, catching the light --------------
   The appbar already closes on a hairline of the alloy. Once every eleven seconds a
   brighter band walks along it — the same light that walks across the bullion on the
   welcome page, a hundred times quieter. */
/* TWO layers, and the order matters: the gleam on top, the rule itself underneath,
   full width and never moving. Written as one animated background it was one layer —
   which meant that for the six seconds the gleam spent parked off the right-hand side,
   `no-repeat` left most of the masthead with no rule under it at all. */
.appbar::after {
  background-image:
    linear-gradient(90deg,transparent,#fffaece6 50%,transparent),
    linear-gradient(90deg,transparent,#d9b45b40 16%,#ffffff17 50%,#d9b45b40 84%,transparent);
  background-size:22% 100%, 100% 100%;
  background-repeat:no-repeat, no-repeat;
  background-position:-30% 0, 0 0;
  animation:nrRule 11s linear infinite; }
@keyframes nrRule {
  0%,56%  { background-position:-30% 0, 0 0; }
  100%    { background-position:130% 0, 0 0; }
}

/* ---- 8. the page arrives in order -----------------------------------------
   The statement already fades in whole (`body.is-ready #boot`). These give the blocks
   inside it their order — the money, then the way into the book, then the book — so
   the page assembles rather than appearing. Under 700ms in total, once, and never on
   a refresh of the figures. */
/* TRANSLATE ONLY, and that is not a matter of taste. The welcome page's arrival
   (`nrLiftIn`) ends on `scale(.988)` -> 1, and a scale applies to every pixel inside
   the element while it runs: for the ~600ms of the animation a 44px control inside a
   scaled block MEASURES 43.5px. The app's own phone rule gives the desk's Re-open chip
   a 44px floor and a test asserts it, so the sheet's entrance was quietly making the
   page fail its own thumb-target contract — caught 17/09/2026 by exactly that test.
   Nothing that can contain a control is ever scaled here. */
@keyframes nrSettle {
  from { opacity:0; transform:translateY(16px); }
  to   { opacity:1; transform:none; }
}
.hero { animation:nrSettle 520ms var(--ease) 40ms both; }
.adm-wrap .adm-card { animation:nrSettle 520ms var(--ease) both; }
.adm-wrap .adm-card:nth-of-type(2) { animation-delay:70ms; }
.adm-wrap .adm-card:nth-of-type(3) { animation-delay:140ms; }
/* TWO THINGS ARE DELIBERATELY NOT IN THAT LIST.
   `.stmt-nav` is `position:sticky`, and an element stays composited for as long as an
   animation is filling it — a composited sticky header stops sticking in Chrome, which
   would cost the page the one control that makes a twenty-thousand-pixel document
   navigable. `.paper` is worse: it CONTAINS sticky table headers
   (`.tbl-scroll.fits table.st thead th`), so animating the sheet would unpin the
   header row of every long table inside it. Both simply arrive with the page. */

/* ---- 9. the label over the field being filled -----------------------------
   The one focus idea the sign-in page has that the rest of the site did not: the
   label warms when the field under it is being typed into. The ring round the field
   itself is already global (`input:focus`) and is not touched here. */
.frow:focus-within > label, .sec-row:focus-within > label { color:var(--gold2); }
select:focus, textarea:focus { border-color:var(--gold);
  box-shadow:inset 0 2px 5px #00000073, 0 0 0 3px #d9b45b2b, 0 0 22px -6px #d9b45b52;
  outline:0; }

/* ---- 10. the dropdown, which was never dressed ----------------------------
   Four <select>s on this site (the two activity filters on the desk console, the role
   and the status in the new-account form) were left in the browser's own clothes: a
   grey OS control nineteen pixels tall on a black page, which is both ugly and, on a
   phone, genuinely hard to hit. They are now the same well every input is, with the
   desk's own chevron — a data: URL, which `img-src 'self' data:` allows — and a target
   a thumb can land on. */
select {
  -webkit-appearance:none; appearance:none;
  background-color:#080b14;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%239aa8c2' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 12px center; background-size:12px 8px;
  border:1px solid var(--line2); border-radius:var(--r2); color:var(--txt);
  font:inherit; font-size:var(--t2); line-height:1.3;
  padding:9px 34px 9px 12px; min-height:42px; max-width:100%; cursor:pointer;
  transition:border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease); }
select:hover { border-color:#425574; }
/* the list itself is drawn by the operating system: these two are the only properties
   it takes from the page, and without them a dark select opens a white menu */
select option { background:#0c111e; color:var(--txt); }
/* the ledger's filter bar re-declares the `background` SHORTHAND at a higher
   specificity, which wipes the chevron — so it is handed back here */
.filter-bar select { background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%239aa8c2' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 12px center;
  background-size:12px 8px; }

/* ---- 11. a phone is a battery ----------------------------------------------
   The motes and the blurred surface are what a phone pays for every frame. Below
   620px the motes go and the glass goes back to being a fill; what is left — the
   drift, the rim, the sweep on the two big buttons — is one composited layer each. */
@media screen and (max-width:620px){
  /* a target a thumb can land on: 44px is the number every platform's own guidance
     gives, and these were the three controls on the site still under it */
  select, .filter-bar input, .filter-bar select { min-height:44px; }
  .chip { min-height:42px; padding-left:var(--s4); padding-right:var(--s4); }
  .nr-dust.fixed { display:none; }
  .hero { -webkit-backdrop-filter:none; backdrop-filter:none;
    background:
      radial-gradient(760px 300px at 10% -45%, #d9b45b21, transparent 70%),
      radial-gradient(560px 300px at 96% 130%, #2a4d8f1f, transparent 70%),
      linear-gradient(168deg,#16203a 0%,#0e1524 46%,#0a0f1b 100%); }
  .hero::after { animation:none; }
}

}  /* @media screen */
