/* ==========================================================================================
   VD20 補習班 ERP — brand.css: the 高竿教育事業集團 decoration system (docs/ui/BRAND-SPEC.md §5, §7)
   Loaded once by partials/head right after app.css, so layouts.app, auth/layout and errors/layout all have it.
   The TOKENS live in app.css §1 (--gg-*, light + dark + print); this file only draws with them.

   Elements (manual p.09 / p.11 — never invent new motifs):
     .gg-rule        the blue | green heading rule (pure CSS, hard 50/50 split)      支撐 the heading
     .gg-fin         the green rising fin (mask of brand/fin.svg)                     支撐 · 上升感
     .gg-stage       a clipped, aria-hidden layer holding .gg-ribbon seeds              延續 (every ribbon bleeds off an edge)
     .gg-ic          sprite glyphs (partials/brand-sprite): blue parts take `fill`, green parts `currentColor`
     .gg-disc        the empty-state glyph disc (seed · rise · bloom · burst)
     .gg-loader      the 成長 dash-ring loader (spins only while it is shown; static ring + text under reduce / off)
     .gg-welcome     the dashboard greeting card's soft stage (≤ --gg-ribbon-a-soft)
     .gg-logo--light / --dark   the light / dark lockup pair, switched by the theme in CSS (no JS)

   WHERE (BRAND-SPEC §5.2): login / 2FA / error stages, the dashboard welcome card, empty states, the sidebar mark,
   loaders. NEVER behind tables, forms, KPI tiles, dialogs, menus, toasts or print; only --text-1 / --text-2 may sit
   over a ribbon. Opacity ceilings are tokens: --gg-ribbon-a .22 / --gg-ribbon-a-green .26 on a stage,
   --gg-ribbon-a-soft .10 inside a card (dark .14 / .16 / .08).

   CSP: no inline style anywhere — every url() is RELATIVE to this file (css/ → ../brand/), so the /erp sub-path
   works; masks load under img-src 'self'. Motion reads the v2 distance tokens and is switched off explicitly for
   精簡 (calm, decoration only), 關閉 (off) and prefers-reduced-motion (§G). Transform / opacity only.
   ========================================================================================== */

/* ── B. the blue | green rule — 48 × 3 under a brand heading, 24 × 2 under an empty-state title ─────────────── */
.gg-rule { display:block; width:48px; height:3px; border-radius:2px; background:var(--gg-rule); transform-origin:left center; }
.gg-rule--sm { width:24px; height:2px; }
.gg-rule--center { margin-left:auto; margin-right:auto; transform-origin:center; }
/* grows once per load (v2 grow-x, 480 ms, after 120 ms) — an entrance: fills backwards, never holds a state */
.gg-rule.is-grow { animation:grow-x var(--dur-5) var(--ease-out) 120ms backwards; }

/* ── C. the green fin (支撐) — always green, upright; never flipped, rotated or outlined ─────────────────────── */
.gg-fin { display:inline-block; width:.6em; aspect-ratio:57.16 / 72.76; margin-left:.18em; vertical-align:0; flex:none;
          background-color:var(--gg-green);
          -webkit-mask:url(../brand/fin.svg) center / contain no-repeat; mask:url(../brand/fin.svg) center / contain no-repeat; }
/* the 上升感 delight: rises after the headline / greeting (or a success state such as 月結 done), once */
.gg-fin.is-rise { animation:gg-rise var(--dur-4) var(--ease-spring) 240ms backwards; }
@keyframes gg-rise { from { opacity:0; transform:translateY(calc(var(--m-rise) * 2)) scale(var(--m-pop)); } to { opacity:1; transform:none; } }

/* ── D. ribbons — the 種子 seed glyph (brand/seed.svg) as a mask; colour + opacity from tokens ────────────────
   Cover-A geometry (fitted to the cover's vectors): blue seed rotate(-41.5deg), green seed rotate(138.5deg); the green
   centre sits (-0.32h, -0.62h) from the blue one (h = the seed's height, --gg-h). A ribbon is positioned by its CENTRE
   (left / top + translate(-50%,-50%)), always bleeds off at least one edge and always sits BEHIND opaque content.
   Markup: <div class="gg-stage" aria-hidden="true"><i class="gg-ribbon gg-ribbon--green …"></i>…</div> */
.gg-stage { position:absolute; inset:0; overflow:hidden; pointer-events:none; z-index:0; }
.gg-ribbon { position:absolute; display:block; height:var(--gg-h, 88vh); aspect-ratio:111.47 / 131.4;
             background-color:var(--gg-blue); opacity:var(--gg-ribbon-a);
             -webkit-mask:url(../brand/seed.svg) center / contain no-repeat; mask:url(../brand/seed.svg) center / contain no-repeat;
             transform:translate(-50%, -50%) rotate(-41.5deg); }
.gg-ribbon--green { background-color:var(--gg-green); opacity:var(--gg-ribbon-a-green); transform:translate(-50%, -50%) rotate(138.5deg); }
/* the slow drift (brand stages only): ≈ 12 px / 1.5°, 22 s (green 26 s, reversed), TWO alternate iterations, then
   rest — no endless loop (spares the old counter PCs) */
.gg-ribbon.is-drift { animation:gg-drift 22s var(--ease-in-out) 1.2s 2 alternate backwards; }
.gg-ribbon--green.is-drift { animation-duration:26s; animation-direction:alternate-reverse; }
@keyframes gg-drift { from { translate:0 0; rotate:0deg; } to { translate:calc(var(--m-rise) * 2) calc(var(--m-rise) * -1.5); rotate:1.5deg; } }

/* ── E. glyphs, empty-state disc, loader, welcome card, lockups ─────────────────────────────────────────────── */
/* sprite colour convention (partials/brand-sprite): blue parts carry no fill (inherit `fill`), green parts are
   fill="currentColor" — one class paints both official colours, in either theme. Usage:
   <svg class="gg-ic" aria-hidden="true" focusable="false"><use href="#gg-seed"/></svg> */
.gg-ic { fill:var(--gg-blue); color:var(--gg-green); stroke:none; flex:none; }

/* empty states: <x-empty art="seed|rise|bloom|burst"> → a 72 px disc (burst 88 px) with the 40 px glyph, then the
   title, then .gg-rule--sm. seed = 尚無資料 · rise = 進行中 / 匯入中 · bloom = 已完成 / 已月結 · burst = 沒有待處理事項 */
.gg-disc { display:grid; place-items:center; width:72px; height:72px; margin:0 auto 16px; border-radius:50%; background:var(--gg-disc); }
.gg-disc > svg { width:40px; height:40px; }
.gg-disc--burst { width:88px; height:88px; }
.gg-disc--burst > svg { width:72px; height:72px; }
/* optional: the glyph floats three times, then rests (v2 M32 float) */
.gg-disc.is-float > svg { animation:float 3.2s var(--ease-in-out) 3 backwards; }

/* loader (busy > 300 ms, the v2 skeleton rule): render it only while something is loading, remove it when done —
   <div class="gg-loader" role="status"><svg class="gg-ic gg-load" aria-hidden="true" focusable="false"><use href="#gg-dashring"/></svg><span class="gg-load-t">載入中…</span></div>
   The ring spins (1.6 s linear — kept under 精簡: it is feedback); under 關閉 / reduce it stands still and the text
   shows. The text is always there for screen readers. */
.gg-loader { display:inline-flex; align-items:center; gap:8px; font-size:var(--fs-sm); color:var(--text-2); }
.gg-load { display:inline-block; width:28px; height:28px; flex:none; }
.gg-loader .gg-load { animation:spin 1.6s linear infinite; }
.gg-load-t { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }

/* the dashboard greeting card ONLY (.gg-welcome): one blue seed + its green partner off the top, 260 px tall at
   --gg-ribbon-a-soft, clipped by the card at its right edge. KPI tiles, charts and tables below stay plain. */
.gg-welcome { position:relative; overflow:hidden; isolation:isolate; }
.gg-welcome > :not(.gg-stage) { position:relative; z-index:1; }
.gg-welcome > .gg-stage .gg-ribbon { --gg-h:260px; opacity:var(--gg-ribbon-a-soft); }
.gg-welcome .gg-ribbon--w1 { left:calc(100% - 150px); top:40%; }                       /* blue ring at the right edge */
.gg-welcome .gg-ribbon--w2 { left:calc(100% - 150px - 83px); top:calc(40% - 161px); }   /* its green partner, off the top */

/* lockups: the light file on white, the derived -white file (wordmark #ECEEF2, BRAND-SPEC §4.6) on the dark theme —
   two <img>s, switched here (no JS). Lockups are block images with width / height attributes (no layout shift). */
.gg-logo--dark { display:none; }
:root[data-theme="dark"] .gg-logo--light { display:none; }
:root[data-theme="dark"] .gg-logo--dark { display:block; }

/* ── G. motion levels (mirrors app.css §7.5) — 精簡 drops decoration, 關閉 / OS reduce drop everything ─────────── */
html[data-motion="calm"] .gg-ribbon.is-drift, html[data-motion="calm"] .gg-rule.is-grow, html[data-motion="calm"] .gg-fin.is-rise,
html[data-motion="calm"] .gg-disc.is-float > svg { animation:none; }
html[data-motion="off"] .gg-ribbon.is-drift, html[data-motion="off"] .gg-rule.is-grow, html[data-motion="off"] .gg-fin.is-rise,
html[data-motion="off"] .gg-disc.is-float > svg, html[data-motion="off"] .gg-loader .gg-load { animation:none !important; }
html[data-motion="off"] .gg-load-t { position:static; width:auto; height:auto; margin:0; overflow:visible; clip:auto; }
@media (prefers-reduced-motion: reduce) {
    .gg-ribbon.is-drift, .gg-rule.is-grow, .gg-fin.is-rise, .gg-disc.is-float > svg, .gg-loader .gg-load { animation:none !important; }
    .gg-load-t { position:static; width:auto; height:auto; margin:0; overflow:visible; clip:auto; }
}

/* forced colours: decoration goes (the lockups and the mark stay — they are images with their own names) */
@media (forced-colors: active) {
    .gg-stage, .gg-fin, .gg-rule, .tb::before { display:none; }
    .gg-disc { background:Canvas; border:1px solid CanvasText; }
}

/* print: always light, no decoration (BRAND-SPEC §4.3 — the colour lockup only, and only where approved) */
@media print {
    .gg-stage, .gg-fin, .gg-rule, .gg-loader { display:none !important; }
    .gg-logo--dark { display:none !important; }
    .gg-logo--light { display:block !important; }
}
