
@font-face{font-family:"Big Shoulders Inline Text";font-style:normal;font-weight:900;font-display:swap;
  src:url(/assets/fonts/big-shoulders-inline-text-900.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:"Onest";font-style:normal;font-weight:100 900;font-display:swap;
  src:url(/assets/fonts/onest-variable.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:"Geist Mono";font-style:normal;font-weight:400 700;font-display:swap;
  src:url(/assets/fonts/geist-mono-variable.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}
:root{color-scheme:dark;
  --bg:#06090F;--card:#0F1424;--elev:#161F33;--line:#1F2A40;--line2:#2A3852;
  --ink:#EDEBE3;--mute:#8A93A7;--faint:#5C677E;
  --accent:#06B6D4;--accent2:#38BDF8;--on-accent:#04141A;--sky:#38BDF8;
  --agn-mark:#5FBFB4;
  --warn:#F59E0B;--on-warn:#1a1206;--ok:#3fbf9f;--danger-ink:#FF8B7D;--danger-line:#5a3130;
  /* Round 127 (ADR-101): the brand faces are back, and self-hosted this time,
     so they actually load instead of being named at a CDN nobody calls. Onest
     for body, Geist Mono for code, Big Shoulders Inline Text for display
     headings. Segoe UI stays as the FIRST fallback, so a surface that has not
     got the face yet still reads in the round 126 face rather than in a
     browser default. Values mirror shared/theme.css in slobal.com. */
  --sans:"Onest","Segoe UI","Segoe UI Variable Text",system-ui,-apple-system,sans-serif;
  --mono:"Geist Mono",ui-monospace,"Cascadia Code",Consolas,monospace;
  --display:"Big Shoulders Inline Text","Big Shoulders Display",sans-serif;}
@media (prefers-color-scheme:light){:root{color-scheme:light;
  --bg:#F5F4EF;--card:#FFFFFF;--elev:#EFEEE7;--line:#E0DED6;--line2:#CDC9BC;
  --ink:#0A0E1A;--mute:#374151;--faint:#6B7385;--accent:#0E7490;--accent2:#0891B2;--on-accent:#FFFFFF;--sky:#0369A1;
  --agn-mark:#03625A;
  --warn:#F59E0B;--on-warn:#1a1206;--ok:#3fbf9f;--danger-ink:#FF8B7D;--danger-line:#5a3130;}}
html[data-theme="dark"]{color-scheme:dark;
  --bg:#06090F;--card:#0F1424;--elev:#161F33;--line:#1F2A40;--line2:#2A3852;
  --ink:#EDEBE3;--mute:#8A93A7;--faint:#5C677E;
  --accent:#06B6D4;--accent2:#38BDF8;--on-accent:#04141A;--sky:#38BDF8;
  --agn-mark:#5FBFB4;
  --warn:#F59E0B;--on-warn:#1a1206;--ok:#3fbf9f;--danger-ink:#FF8B7D;--danger-line:#5a3130;}
html[data-theme="light"]{color-scheme:light;
  --bg:#F5F4EF;--card:#FFFFFF;--elev:#EFEEE7;--line:#E0DED6;--line2:#CDC9BC;
  --ink:#0A0E1A;--mute:#374151;--faint:#6B7385;--accent:#0E7490;--accent2:#0891B2;--on-accent:#FFFFFF;--sky:#0369A1;
  --agn-mark:#03625A;
  --warn:#F59E0B;--on-warn:#1a1206;--ok:#3fbf9f;--danger-ink:#FF8B7D;--danger-line:#5a3130;}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html{-webkit-text-size-adjust:100%}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
body{margin:0;font:16px/1.55 var(--sans);background:var(--bg);color:var(--ink);min-height:100vh;
  padding:0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  font-feature-settings:"kern" 1,"liga" 1;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;position:relative}

.topbar{display:flex;align-items:center;gap:12px;min-height:50px;padding:env(safe-area-inset-top) calc(16px + env(safe-area-inset-right)) 0 calc(16px + env(safe-area-inset-left));background:var(--card);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:10}
.topbar::after{content:'';position:absolute;left:0;right:0;bottom:-1px;height:1px;background:linear-gradient(90deg,transparent,rgba(6,182,212,.5) 40%,rgba(56,189,248,.4) 62%,transparent)}
/* Round 101 (TagFix sweep): the top-left of every surface is the FULL Slobal
   wordmark - the logotype, not the bare torus - and the whole brand slot is a
   LINK to the apex home. The letters are drawn in currentColor so the ◐ toggle
   recolours them with the rest of the bar; the O keeps its gradient in both
   themes. A product name (a workspace, or "Helmsly" on the offline page)
   follows behind a hairline divider, Entra style, and IT is the part that
   ellipsises - the logotype itself never squeezes or wraps. .brand shrinks
   (flex:0 1 auto + min-width:0) so a long workspace name can never widen the
   page; relay-fit.mjs holds that at an iPhone-12 viewport. */
.brand{display:flex;align-items:center;gap:10px;font-weight:700;letter-spacing:-.015em;font-size:15px;flex:0 1 auto;min-width:0;color:var(--ink);text-decoration:none}
a.brand:hover{color:var(--ink);text-decoration:none}
.brand .slobal-wordmark,.brand img,.brand .bsep{flex:none}
.brand .bsep{width:1px;height:19px;background:var(--line2)}
.brand .pname{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The identity cluster must SHRINK, never widen the page: long emails
   ellipsise on narrow screens (QA-1..5 - every signed-in surface scrolled
   horizontally on a phone because this row had no min-width:0 path). */
.who{margin-left:auto;display:flex;align-items:center;gap:9px;font-size:13px;color:var(--mute);min-width:0;flex:0 1 auto}
.who>span,.who>a{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.who>a.act,.who .themetoggle,.who .slbadge{flex:none;overflow:visible}
/* The avatar keeps the flex:none and NOT the overflow:visible above.
   Round 128: that one declaration is what made the corner picture a hard
   square on every relay surface while the hub and AgnCred drew a circle.
   border-radius does not clip a REPLACED element's content on its own - the
   UA sheet's img{overflow:clip} is what does, and overflow:visible here
   overrode it, so the photo painted right out to its own corners with the
   round drawn behind it. (display:flex on the same element is harmless, and
   display:block alone does NOT bring the clip back - both measured.)
   The reset exists to undo the ellipsis rule one line up, whose selector is
   .who>span,.who>a - DIRECT children. Neither avatar is one: both sit inside
   a.whoami. So this element never needed the reset in the first place. */
.who .avatar{flex:none}
/* The identity IS the way to My Account (panel parity): the picture and the
   name, one link, no separate button beside them. 13.5px/600 in a 260px
   maximum - the panel's .acct, value for value. */
.who a.whoami{display:inline-flex;align-items:center;gap:8px;color:var(--mute);font-size:13.5px;font-weight:600;max-width:260px;text-decoration:none}
.who a.whoami:hover{color:var(--ink);text-decoration:none}
.who a.whoami .em{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media(max-width:600px){.topbar{gap:8px}.who{gap:7px}.who .slbadge{display:none}.brand{gap:8px}.brand .slobal-wordmark{height:18px}.brand .bsep{height:16px}.brand .pname{font-size:14px}}
/* Identical to the Helmsly panel's theme button: the constant ◐ glyph in the
   same rounded-rect chrome as every other header button; current mode lives
   in the tooltip, not the glyph. Panel size: a 34px square, 8px radius. */
.themetoggle{background:var(--elev);color:var(--ink);border:1px solid var(--line2);border-radius:8px;width:34px;height:34px;padding:0;font:15px/1 var(--sans);cursor:pointer;transition:border-color .16s,color .16s;display:inline-flex;align-items:center;justify-content:center;flex:none}
.themetoggle:hover{border-color:var(--accent)}
/* The PORTAL pill, verbatim from the Helmsly panel chrome (34px tall, 13px
   sides): the way home from every SIGNED-IN surface, beside the theme button,
   exactly as the panel's own #slhome is. Round 101 took it off anonymous
   pages - there is no session home to go to from a sign-in door, and the
   brand link in the top-left is the way to the front of the platform.
   Round 102 hides it on the portal itself, and the hidden state has to be
   spelled out here: the UA's own [hidden]{display:none} loses to any author
   display value, so without this rule a hidden pill would still paint. */
.slbadge{display:inline-flex;align-items:center;height:34px;padding:0 13px;border:1px solid var(--line2);border-radius:999px;background:var(--elev);font:800 12px/1 var(--sans);letter-spacing:.08em;color:var(--mute);flex:none;text-decoration:none;transition:border-color .16s,color .16s}
.slbadge[hidden]{display:none}
a.slbadge:hover{color:var(--ink);border-color:var(--accent);text-decoration:none}
/* One Sign Out control for the whole platform: a quiet bordered button, the
   right-most element of the cluster, same class and shape on all surfaces -
   the panel's small ghost button (28px tall, 10px radius, 13px). */
.who a.signout{text-decoration:none;white-space:nowrap;height:28px;padding:0 11px;border-radius:10px;font-size:13px;background:transparent;margin:0}
/* ONE class, TWO elements, and until round 128 one rule for both: the stored
   PHOTO (an img.avatar) and the coloured INITIAL that stands in when there is
   none (a span.avatar). This rule is the geometry they genuinely share - the
   size, the round, and the letter's own colouring and centring, which an image
   simply ignores. Every size is still written here and only here; the rule
   that follows adds no geometry of its own. */
.avatar{width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,var(--accent),var(--accent2));color:var(--on-accent);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12px}
/* The PICTURE's own rule, built the way the two surfaces the operator calls
   correct build theirs - the hub's .acctpic (app/ui/v3/tokens.css) and
   AgnCred's img.admin-avatar (apps/web/src/styles.css). Both put the round on
   the image itself with display:block and object-fit:cover, and neither lets
   overflow off the UA's clip. That last part is the whole defect: a replaced
   element paints its content through a border-radius unless the overflow keeps
   it in, so this rule STATES the clip rather than trusting that nobody
   overrides it again. display:block retires the inline-baseline gap; the two
   member grids override it with their own inline-block, because there the
   picture really does sit on a line of text (global-admin.mjs, tenant-admin.mjs
   - the round now comes from HERE and only the flow does not). object-fit:cover
   lives here too, so no caller writes it inline any more. */
img.avatar{display:block;overflow:hidden;object-fit:cover}
/* The identity picture in the TOP BAR is chrome, not content: it sits inside
   the 34px cluster at the panel's own .acctpic size (18px round), so it is
   sized here once rather than by whatever number each surface passed. The
   28px .avatar above stays the size the account page's own forms use.
   The 8px initial is the account page's own ratio (size / 2.3) at 18.
   Round 105 (tags 02-04): these two numbers are now the ONLY place the
   corner picture is sized. The account page used to write them inline as
   well, from its own arithmetic, where the cascade let them win - so WHOAMI
   below emits the picture with no width, height or font-size of its own and
   a test in server.test.mjs holds every surface to that. */
.who .avatar{width:18px;height:18px;font-size:8px}
body::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(1100px 520px at 50% -10%,rgba(6,182,212,.06),transparent 60%)}
body::after{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;opacity:.022;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
.wrap{max-width:440px;margin:0 auto;padding:56px 22px}
.wide{max-width:960px}
.logo{font-size:38px;text-align:center;margin-bottom:8px;filter:drop-shadow(0 2px 10px rgba(6,182,212,.28))}
/* A hero that is an outline GLYPH rather than the Slobal mark: no brand glow
   behind it, and no inline line box from the mark-sized font. */
.logo.glyph{filter:none;font-size:0;line-height:1}
/* The page title is the one place the brand DISPLAY face speaks (round 127,
   ADR-101). It is an inline face and already condensed, so it takes positive
   tracking: the round 126 -.025em was drawn for Segoe and closes the inline
   strokes up until the counters fill in. */
h1{font-size:23px;margin:0 0 4px;text-align:center;letter-spacing:.02em;font-family:var(--display);font-weight:900;text-transform:uppercase}
.sub{color:var(--mute);text-align:center;margin:0 0 26px;font-size:14px;line-height:1.5}
.card{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:20px}
.btn{display:flex;align-items:center;justify-content:center;gap:9px;width:100%;border:0;border-radius:8px;padding:12px;font:600 15px var(--sans);cursor:pointer;text-decoration:none;margin:0 0 10px;transition:border-color .16s,background .16s,filter .16s}
.btn.sso{background:var(--elev);color:var(--ink);border:1px solid var(--line2)}
.btn.sso:hover{border-color:var(--accent)}
.btn.primary{background:var(--accent);color:var(--on-accent)}
.btn.primary:hover{filter:brightness(1.08)}
/* The app-icon slot, same declaration the admin shell carries (round 86, tag
   03): the tenant front door names an app on a button, so its mark comes from
   APP_ICON like every other app mark on the platform rather than from a raw
   FLAG at a hand-picked size. */
.appic{flex:none;display:inline-flex;align-items:center;justify-content:center}
.appic>svg{display:block}
.muted{color:var(--faint);font-size:12.5px;text-align:center;margin-top:20px;line-height:1.5}
.muted2{color:var(--mute)}
input{width:100%;background:var(--bg);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:12px 14px;font:inherit;margin:0 0 10px;transition:border-color .16s,box-shadow .16s}
input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(6,182,212,.14)}
table{width:100%;border-collapse:collapse;font-size:14px;font-variant-numeric:tabular-nums lining-nums}
th,td{text-align:left;padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:top}
th{font-size:10.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--faint);font-weight:650}
.chip{display:inline-block;font:600 10.5px/1 var(--mono);letter-spacing:.03em;padding:3px 8px;border-radius:4px;border:1px solid var(--line2);margin:1px 2px;color:var(--mute)}
.chip.on{color:var(--accent);border-color:var(--accent)}.chip.off{color:var(--warn);border-color:var(--warn)}
.act{display:inline-flex;align-items:center;gap:6px;background:var(--elev);color:var(--ink);border:1px solid var(--line2);border-radius:6px;padding:5px 10px;font:500 12px var(--sans);cursor:pointer;margin:1px;transition:border-color .14s,background .14s}
.act:hover{border-color:var(--accent)}
.act.danger{color:var(--danger-ink);border-color:var(--danger-line)}
.act.go{background:var(--accent);color:var(--on-accent);border:1px solid var(--accent);font-weight:600}
.row{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:8px 0}
.pill{font-size:12px;color:var(--mute)}
a{color:var(--sky);text-decoration:none}a:hover{text-decoration:underline;text-underline-offset:2px}
.tier{border:1px solid var(--line);border-radius:8px;padding:16px;margin:0 0 12px;background:var(--bg)}
.tier h3{margin:0 0 2px;font-size:12px;text-transform:uppercase;letter-spacing:.07em;color:var(--accent)}
.tier .p{font-size:21px;font-weight:700;margin:4px 0;letter-spacing:-.02em}
@media(pointer:coarse){.btn{min-height:48px}.act{min-height:44px;padding:10px 14px}input,select{min-height:46px;font-size:16px}}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}
/* ROUND 129 (ADR-102): design-system layer, scoped to html[data-ds]. */
/* Theme-dependent ds values. Base = DARK. Origins in the ds spec: BADGES
   ("tinted background with dark text"), TOKENS (--ok #067647, --ok-bg #ECFDF3,
   --warn #B54708, --warn-bg #FFFAEB, --neutral-bg #EFEEE8) and the Slobal
   context's --primary-tint #E3F1F5. The shell has a token for none of them.
   On a dark ground a tint glows, so a dark badge is a transparent ground with
   a coloured border and coloured text, and a dark card takes no shadow at all
   (a shadow on a dark ground is mud). */
html[data-ds]{
  --ds-ok-ink:var(--ok);--ds-ok-bg:transparent;--ds-ok-line:var(--ok);
  --ds-warn-ink:var(--warn);--ds-warn-bg:transparent;--ds-warn-line:var(--warn);
  --ds-neutral-ink:var(--mute);--ds-neutral-bg:transparent;--ds-neutral-line:var(--line2);
  --ds-tile-hover:var(--elev);
  --ds-shadow:none}
@media (prefers-color-scheme:light){html[data-ds]:not([data-theme="dark"]){
  --ds-ok-ink:#067647;--ds-ok-bg:#ECFDF3;--ds-ok-line:#ECFDF3;
  --ds-warn-ink:#B54708;--ds-warn-bg:#FFFAEB;--ds-warn-line:#FFFAEB;
  --ds-neutral-ink:var(--mute);--ds-neutral-bg:#EFEEE8;--ds-neutral-line:#EFEEE8;
  --ds-tile-hover:#E3F1F5;
  --ds-shadow:0 1px 2px rgba(10,14,26,.05)}}
html[data-ds][data-theme="light"]{
  --ds-ok-ink:#067647;--ds-ok-bg:#ECFDF3;--ds-ok-line:#ECFDF3;
  --ds-warn-ink:#B54708;--ds-warn-bg:#FFFAEB;--ds-warn-line:#FFFAEB;
  --ds-neutral-ink:var(--mute);--ds-neutral-bg:#EFEEE8;--ds-neutral-line:#EFEEE8;
  --ds-tile-hover:#E3F1F5;
  --ds-shadow:0 1px 2px rgba(10,14,26,.05)}
/* TYPE ROLES (ds spec TYPE SCALE). The display face speaks once per page, on
   the page title, at the spec's own 46/34 and 1.05. It is NOT uppercased here:
   the casing law puts uppercase in eyebrows and table headers only, and the
   display role is not one of them. .subhead keeps its own voice, so the two
   heading roles step around it rather than overriding a component. */
html[data-ds] body{font-size:15px;line-height:1.6}
html[data-ds] .pagetitle,html[data-ds] h2.pagetitle{font-family:var(--display);font-weight:900;font-size:46px;line-height:1.05;letter-spacing:.02em;text-transform:none;color:var(--ink);margin:0 0 12px}
html[data-ds] .ds-h2,html[data-ds] h2:not(.pagetitle):not(.subhead){font-family:var(--sans);font-weight:700;font-size:26px;line-height:1.25;letter-spacing:-.01em;color:var(--ink);margin:0 0 12px}
html[data-ds] .ds-h3,html[data-ds] h3:not(.subhead){font-family:var(--sans);font-weight:600;font-size:18px;line-height:1.35;letter-spacing:0;color:var(--ink);margin:0 0 8px}
html[data-ds] .ds-eyebrow{display:block;font:600 11px/1.4 var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--accent);margin:0 0 6px}
html[data-ds] .ds-small{font-size:13px;line-height:1.5;color:var(--mute)}
html[data-ds] .ds-data{font:400 13px/1.5 var(--mono);font-variant-numeric:tabular-nums lining-nums;color:var(--ink)}
/* SPEC LIST (ds spec COMPONENTS: label-value pairs, 11px uppercase tracked
   labels, values in body or mono, 1px separators). Both themes get the same
   shape because every colour in it is a theme token; on a phone the label
   stacks over its value rather than squeezing the value to nothing. */
html[data-ds] .ds-spec-list{display:block;margin:0;padding:0;border-top:1px solid var(--line)}
html[data-ds] .ds-spec-row{display:flex;gap:16px;align-items:baseline;flex-wrap:wrap;padding:10px 0;border-bottom:1px solid var(--line)}
html[data-ds] .ds-spec-label{flex:none;width:180px;font:600 11px/1.5 var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--faint)}
html[data-ds] .ds-spec-value{flex:1;min-width:0;font-size:15px;line-height:1.6;color:var(--ink)}
html[data-ds] .ds-spec-value.ds-data{font:400 13px/1.6 var(--mono);font-variant-numeric:tabular-nums lining-nums}
@media(max-width:720px){html[data-ds] .ds-spec-row{gap:2px}html[data-ds] .ds-spec-label{width:100%}}
/* CARD (ds spec: surface, 1px line border, radius 8, padding 24). Borders over
   shadows: the shadow ceiling rides --ds-shadow, which is the spec's
   0 1px 2px rgba(10,14,26,.05) on light and nothing at all on dark. */
html[data-ds] .ds-card{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:24px;box-shadow:var(--ds-shadow)}
/* BADGE (ds spec: pill, 24px, dot indicator). The dot is currentColor, so a
   variant states its colour once and the dot follows. */
html[data-ds] .ds-badge{display:inline-flex;align-items:center;gap:7px;height:24px;padding:0 11px;border-radius:999px;font:600 12px/1 var(--sans);white-space:nowrap;background:var(--ds-neutral-bg);border:1px solid var(--ds-neutral-line);color:var(--ds-neutral-ink)}
html[data-ds] .ds-badge::before{content:"";flex:none;width:6px;height:6px;border-radius:50%;background:currentColor}
html[data-ds] .ds-badge.ok{background:var(--ds-ok-bg);border-color:var(--ds-ok-line);color:var(--ds-ok-ink)}
html[data-ds] .ds-badge.warn{background:var(--ds-warn-bg);border-color:var(--ds-warn-line);color:var(--ds-warn-ink)}
html[data-ds] .ds-badge.neutral{background:var(--ds-neutral-bg);border-color:var(--ds-neutral-line);color:var(--ds-neutral-ink)}
/* APP TILE, converged on the ds card metrics. Ground, border, radius and focus
   only: the inner layout (the app mark and its .nm label) and the tile's size
   are untouched, so no icon and no click target moves. */
html[data-ds] .tile.app{background:var(--card);border:1px solid var(--line);border-radius:8px}
html[data-ds] .tile.app:hover{border-color:var(--accent);background:var(--ds-tile-hover)}
html[data-ds] .tile.app:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* DENSITY (ds spec SPACING: section 64 desktop, 48 mobile; body measure 640). */
html[data-ds] .ds-section{margin:0 0 64px}
html[data-ds] .ds-section:last-child{margin-bottom:0}
html[data-ds] .pagedesc,html[data-ds] .ds-measure{max-width:640px}
@media(max-width:720px){html[data-ds] .ds-section{margin-bottom:48px}}
/* This layer introduces no animation. The sheets above already kill every
   transition under reduced motion with !important; the tile is named again
   here so the one hover this layer touches states its own answer. */
@media (prefers-reduced-motion:reduce){html[data-ds] .tile.app{transition:none}}
/* ROUND 130: the component vocabulary the account surface needs - buttons,
   form fields, data tables and the badge's bad state. APPENDED, never edited
   into the block above, so the round 129 sheet stays a byte-prefix of this one
   and a reader can still see round 129 whole. Everything below is scoped under
   html[data-ds] exactly as everything above it is, so a surface that has not
   opted in still paints what it painted before.
   One correction to the note four lines up: this round DOES introduce hover
   transitions, on the button and the input. The reduced-motion guard at the
   foot of this block names them, and the sheets above already kill every
   transition under reduced motion with !important, so the guard is a statement
   rather than a fix. */
/* New theme-dependent ds values, in the same three-block pattern the head of
   this layer uses: base carries DARK (this shell's default), the
   prefers-color-scheme:light block that excludes a forced dark theme carries
   LIGHT, and html[data-ds][data-theme="light"] carries it again so the toggle
   wins in both directions. Origins in the ds spec: TOKENS (--bad #B42318,
   --bad-bg #FEF3F2), the Slobal context's --primary-hover #0B5C73, and the
   DATA TABLE line ("hover row tint --paper", which is this shell's --bg).
   The shell carries a token for none of them. It does carry --danger-ink and
   --danger-line, but they are ONE salmon-on-maroon pair used in both themes:
   drawn for the dark ground, and #FF8B7D on white is nowhere near readable. So
   dark keeps the shell's pair and light takes the spec's red, which is what
   the ok and warn trios above already do. */
html[data-ds]{
  --ds-bad-ink:var(--danger-ink);--ds-bad-bg:transparent;--ds-bad-line:var(--danger-line);
  --ds-bad-edge:var(--danger-line);
  --ds-row-hover:var(--elev);
  --ds-input-bg:var(--elev);
  --ds-accent-hover:var(--accent2)}
@media (prefers-color-scheme:light){html[data-ds]:not([data-theme="dark"]){
  --ds-bad-ink:#B42318;--ds-bad-bg:#FEF3F2;--ds-bad-line:#FEF3F2;
  --ds-bad-edge:#B42318;
  --ds-row-hover:var(--bg);
  --ds-input-bg:var(--card);
  --ds-accent-hover:#0B5C73}}
html[data-ds][data-theme="light"]{
  --ds-bad-ink:#B42318;--ds-bad-bg:#FEF3F2;--ds-bad-line:#FEF3F2;
  --ds-bad-edge:#B42318;
  --ds-row-hover:var(--bg);
  --ds-input-bg:var(--card);
  --ds-accent-hover:#0B5C73}
/* BADGE, bad state (ds spec BADGES: "States: ok, warn, bad, neutral, brand").
   Same three properties the ok and warn variants take, so the family reads as
   one set: a tinted ground with dark ink in light, a transparent ground with a
   coloured border and coloured text in dark. */
html[data-ds] .ds-badge.bad{background:var(--ds-bad-bg);border-color:var(--ds-bad-line);color:var(--ds-bad-ink)}
/* BUTTONS (ds spec COMPONENTS: "primary (solid --primary, white text),
   secondary (surface, --primary text, line border, tint on hover), ghost.
   Heights 40 and 32", radius 6 from SPACING AND LAYOUT). Every variant keeps
   the same 1px box, so a row of mixed variants lines up on one baseline and
   nothing shifts when a button changes variant. The solid ground is
   var(--accent) over var(--on-accent), which resolves to teal with white text
   in light and cyan with near-black text in dark: the spec's ban on white on
   cyan is answered by the token pair itself, not by a second rule here. The
   ds law of one primary per view is the page's to keep, not the sheet's.
   Labels are the page's too: sentence case, stating the outcome. */
html[data-ds] .ds-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:40px;padding:0 16px;border:1px solid var(--accent);border-radius:6px;background:var(--accent);color:var(--on-accent);font:600 14px/1 var(--sans);text-align:center;text-decoration:none;white-space:nowrap;cursor:pointer;transition:background .16s,border-color .16s,color .16s}
html[data-ds] .ds-btn:hover{background:var(--ds-accent-hover);border-color:var(--ds-accent-hover);text-decoration:none}
html[data-ds] .ds-btn-sm{height:32px;padding:0 12px;font-size:13px}
html[data-ds] .ds-btn-secondary{background:var(--card);border-color:var(--line);color:var(--accent)}
html[data-ds] .ds-btn-secondary:hover{background:var(--ds-tile-hover);border-color:var(--accent);color:var(--accent)}
html[data-ds] .ds-btn-ghost{background:transparent;border-color:transparent;color:var(--accent)}
html[data-ds] .ds-btn-ghost:hover{background:var(--ds-tile-hover);border-color:transparent;color:var(--accent)}
/* The destructive variant. Quiet at rest (a hairline and coloured text, no
   filled red shouting from the page) and solid only under the pointer, which
   is the last thing a person crosses on the way to a confirm. The account
   page's own arm-then-confirm mechanics are untouched: this is the skin, the
   two-step stays where it is written. */
html[data-ds] .ds-btn-danger{background:transparent;border-color:var(--ds-bad-edge);color:var(--ds-bad-ink)}
html[data-ds] .ds-btn-danger:hover{background:var(--ds-bad-ink);border-color:var(--ds-bad-ink);color:var(--bg)}
html[data-ds] .ds-btn:disabled,html[data-ds] .ds-btn[aria-disabled="true"]{opacity:.55;pointer-events:none}
/* FORM FIELD (ds spec COMPONENTS: "label above (13px, 600, --heading), input
   40px height, radius 6, line border, focus outline 2px --primary offset 2px,
   help text 13px --muted below. Error state: --bad border, help text states
   what happened and how to fix it."). The label sits above rather than beside,
   which is what the spec asks for and what a phone needs. The ground is --card
   in light and --elev in dark: on light the field sits ON a card and a --card
   ground would need the border to carry the whole shape, on dark a recessed
   ground is the thing that reads as an input at all.
   The shells above give every input outline:none and a 3px cyan ring on focus.
   These rules are more specific and later, so a ds input takes the spec's
   outline instead; the ring is cleared explicitly rather than left to fight. */
html[data-ds] .ds-field{display:flex;flex-direction:column;gap:6px;margin:0 0 16px}
html[data-ds] .ds-label{font:600 13px/1.4 var(--sans);color:var(--ink)}
html[data-ds] .ds-input{width:100%;height:40px;padding:0 12px;border:1px solid var(--line);border-radius:6px;background:var(--ds-input-bg);color:var(--ink);font:400 14px/1.4 var(--sans);transition:border-color .16s}
html[data-ds] select.ds-input{padding-right:8px}
html[data-ds] textarea.ds-input{height:auto;min-height:96px;padding:10px 12px;line-height:1.5;resize:vertical}
html[data-ds] .ds-input:focus{outline:2px solid var(--accent);outline-offset:2px;border-color:var(--accent);box-shadow:none}
html[data-ds] .ds-input::placeholder{color:var(--faint)}
html[data-ds] .ds-help{font:400 13px/1.5 var(--sans);color:var(--mute)}
html[data-ds] .ds-field-error>.ds-input{border-color:var(--ds-bad-ink)}
html[data-ds] .ds-field-error>.ds-help{color:var(--ds-bad-ink)}
/* DATA TABLE (ds spec COMPONENTS: "uppercase tracked 11px headers in --muted,
   1px row borders, hover row tint --paper, mono for identifiers, numbers right
   aligned in mono"). Two hooks for one treatment: .ds-table on a table the
   page writes fresh, and .ds-tablecard on a card that ALREADY holds a table,
   so the workspaces and orders tables on the account page adopt the ds skin by
   gaining one class on their wrapper rather than by having every row rewritten.
   The tracked headers and the mono cells are the two places the casing law
   allows uppercase and the type scale asks for the mono face. */
html[data-ds] .ds-table,html[data-ds] .ds-tablecard>table{width:100%;border-collapse:collapse;font-variant-numeric:tabular-nums lining-nums}
html[data-ds] .ds-table th,html[data-ds] .ds-tablecard>table th{font:600 11px/1.4 var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--faint);text-align:left;padding:8px 10px;border-bottom:1px solid var(--line)}
html[data-ds] .ds-table td,html[data-ds] .ds-tablecard>table td{font-size:13px;line-height:1.5;color:var(--ink);text-align:left;padding:10px;border-bottom:1px solid var(--line);vertical-align:top}
html[data-ds] .ds-table tbody tr:hover,html[data-ds] .ds-tablecard>table tbody tr:hover{background:var(--ds-row-hover)}
html[data-ds] .ds-mono{font:400 13px/1.5 var(--mono);font-variant-numeric:tabular-nums lining-nums}
html[data-ds] .ds-num{text-align:right;font:400 13px/1.5 var(--mono);font-variant-numeric:tabular-nums lining-nums}
/* A cell rule beats a class rule on specificity, so the two cell roles are
   named again AS cells; without this a td.ds-mono would keep the body face. */
html[data-ds] .ds-table td.ds-mono,html[data-ds] .ds-tablecard>table td.ds-mono{font:400 13px/1.5 var(--mono)}
html[data-ds] .ds-table td.ds-num,html[data-ds] .ds-tablecard>table td.ds-num{text-align:right;font:400 13px/1.5 var(--mono)}
/* Touch. The shells above already raise their own controls on a coarse
   pointer; the ds controls answer the same law, and the 16px input keeps a
   phone browser from zooming the page on focus. */
@media(pointer:coarse){
  html[data-ds] .ds-btn{min-height:44px}
  html[data-ds] .ds-btn-sm{min-height:40px}
  html[data-ds] .ds-input{min-height:46px;font-size:16px}
  html[data-ds] textarea.ds-input{min-height:96px}}
/* The two hovers this round adds, named for the same reason the tile above is
   named: each states its own answer under reduced motion. */
@media (prefers-reduced-motion:reduce){html[data-ds] .ds-btn,html[data-ds] .ds-input{transition:none}}
/* ROUND 131 fit fix: the display face is wide, and a long one-word pagetitle
   (Customisation) at the 34px phone size is wider than a 390px viewport,
   which fails the relay-fit gate in hub CI (v3-boot-smoke renders every
   section view at 390). Below 720px the title scales with the viewport,
   clamped so it is 34px wherever 34px fits (anything wider than about 440px)
   and shrinks only on narrow phones; overflow-wrap is the safety net so no
   future title can widen the page whatever its length. */
@media (max-width:720px){
  html[data-ds] .pagetitle,html[data-ds] h2.pagetitle{font-size:clamp(26px,7.7vw,34px);overflow-wrap:anywhere}
}
/* ROUND 133 (estate sweep, tag 01): on a phone the topbar cannot hold the
   wordmark, a workspace name, the theme square, an avatar, a person's name
   and Sign Out; both names ellipsized to two characters ("No...", "Sa..."),
   which reads as noise. Below 480px the pagetitle already names the
   workspace and the avatar already names the person, so the topbar drops
   the two text copies and keeps the marks. */
@media (max-width:480px){
  html[data-ds] .brand .pname,html[data-ds] .brand .bsep{display:none}
  html[data-ds] .who .whoami .em{display:none}
}
