/* Minimal app CSS for the AntikCocuk.V5 shell. */

/* ===========================================================================
   COLOURS AND THEMES — read docs/design.md before adding a colour anywhere (#418).

   Every colour in this app comes from a FluentUI design token (var(--colorNeutralBackground1),
   var(--colorStatusDangerForeground1), …). The user picks Açık / Koyu / Otomatik in the header;
   FluentUI swaps the whole token set and sets body[data-theme="dark"] in the dark theme, so a
   token-based rule is right in both themes with no extra CSS. A literal colour (#fff, rgba(),
   white) stays the same in both and is refused by ColourLiteralBaselineTests unless it carries a
   "theme-literal: reason" comment.
   =========================================================================== */

/* ===========================================================================
   PRE-JS TOKEN FALLBACK (#408 §4.5, dark half #418)

   `default-fuib.css` is a RESET, not a token sheet: MEASURED on the pinned rc.4 copy it
   declares 0 design tokens and consumes 36 through var(). The real token set is built by
   FluentUI's lib.module.js — its theme sheet in beforeWebStart, its alias sheet (--success …)
   in afterWebStarted — i.e. only AFTER blazor.web.js has downloaded, imported that module and
   started. Until then every var(--…) in default-fuib.css and in this file resolves to nothing,
   so the prerendered first frame of every page paints in the browser's default serif with no
   token colours.

   MEASURED with blazor.web.js and lib.module.js blocked, on /hesap/giris:
     without this block   body = Times New Roman 16px, transparent background, black text,
                          --colorNeutralBackground1 empty
     with it              body = Segoe UI 14px, #ffffff on #242424, token resolves
   The list is what the shell itself reads before anything else has rendered: the body font
   stack and size, page background and text colour, links, the scrollbar pair, and the two
   radii this file uses. It is a floor, not a copy of the theme — do not grow it. The values
   are the library's own for 5.0.0-rc.5-26219.1 (read from its adopted sheet in both modes).

   The three BRAND tokens (--colorBrandBackground, its Hover, --colorNeutralForegroundOnBrand)
   are the one addition that is not shell (#418). Sign-in is the first page a signed-out user
   acts on, and its "Giriş Yap" is a plain <button> painted from exactly those three, not a
   <fluent-button>. Without them its background is invalid at computed-value time, so it falls
   back to transparent and the button reads as a line of plain text until the library starts.
   MEASURED on /hesap/giris with blazor.web.js blocked: button background rgba(0, 0, 0, 0)
   before, rgb(15, 108, 189) with white text after (Koyu: rgb(17, 94, 163)).

   LIGHT OR DARK is decided by body[data-theme="dark"], which js/theme-boot.js sets before the
   first paint from the same persisted mode the library then reads — so this floor, the library
   and every [data-theme="dark"] rule agree from frame one. It used to be
   @media (prefers-color-scheme: dark), which is wrong for anyone who picked Açık on a dark
   device or Koyu on a light one. :has() keeps the selector on :root, the element the library's
   own sheet targets, which is what the layer below needs.

   ⚠️ @layer IS LOAD-BEARING. This block must LOSE to the library once its sheet lands, and
   source order does NOT achieve that: MEASURED on a dark-mode OS, an unlayered `:root` here
   beat the adopted sheet and pinned --colorNeutralBackground1 to #ffffff while the library
   had resolved dark (#292929) — document.adoptedStyleSheets are ordered BEFORE the
   document's own stylesheets, not after. A cascade layer fixes it from the other side:
   unlayered author styles always beat layered ones, whatever the order. So these tokens
   apply only while nothing else defines them, which is exactly the window they are for.
   Never lift them out of the layer, and never add !important.
   =========================================================================== */
@layer antik-token-fallback {
    :root {
        /* theme-literal-block: pre-JS copy of the library's light token values */
        --fontFamilyBase: 'Segoe UI', 'Segoe UI Web (West European)', -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;
        --fontSizeBase300: 14px;
        --lineHeightBase300: 20px;
        --fontWeightRegular: 400;
        --fontWeightMedium: 600;
        --colorNeutralBackground1: #ffffff;
        --colorNeutralBackground2: #fafafa;
        --colorNeutralForeground1: #242424;
        --colorNeutralForeground4: #707070;
        --colorNeutralStroke1: #d1d1d1;
        --colorBrandForegroundLink: #115ea3;
        --colorBrandForegroundLinkHover: #0f548c;
        --colorBrandForegroundLinkSelected: #115ea3;
        --colorBrandBackground: #0f6cbd;
        --colorBrandBackgroundHover: #115ea3;
        --colorNeutralForegroundOnBrand: #ffffff;
        --borderRadiusMedium: 4px;
        --borderRadiusCircular: 10000px;
    }

    :root:has(> body[data-theme="dark"]) {
        /* theme-literal-block: pre-JS copy of the library's dark token values */
        --colorNeutralBackground1: #292929;
        --colorNeutralBackground2: #1f1f1f;
        --colorNeutralForeground1: #ffffff;
        --colorNeutralForeground4: #999999;
        --colorNeutralStroke1: #666666;
        --colorBrandForegroundLink: #479ef5;
        --colorBrandForegroundLinkHover: #62abf5;
        --colorBrandForegroundLinkSelected: #479ef5;
        --colorBrandBackground: #115ea3;
        --colorBrandBackgroundHover: #0f6cbd;
        /* --colorNeutralForegroundOnBrand is #ffffff in both themes: the light value above serves. */
    }
}

/* Native controls, scrollbars, the date picker and the canvas behind the page follow the theme
   too. FluentUI sets no color-scheme of its own (0 occurrences in rc.5's lib.module.js), so
   without this a plain <input> or <select> stays light-on-dark in Koyu. Keyed on the same
   library-owned attribute as the floor above. */
:root {
    color-scheme: light;
}

:root:has(> body[data-theme="dark"]) {
    color-scheme: dark;
}

/* ===========================================================================
   APP COLOUR PROPERTIES — the only custom colour properties this app defines. Each one is a
   NAME for a FluentUI token, never a colour of its own, so it follows the theme for free
   (a var() in a custom property on :root is resolved on :root, where the library's tokens
   live, and re-resolves when the theme changes). Listed in docs/design.md.

   Home feed item kinds — HomeFeedDisplay.KindClass, shared by TaskList's chips and
   WeeklyCalendar's dots and legend so the two always agree. Four hues for eight kinds:
     teach            Öğretim
     review           GAG, Onay, Atama
     note             Not
     task             Anket, Görüşme, Ön kayıt, Görev
   -mark  a solid dot / bar / swatch     (palette BorderActive: saturated in both themes)
   -tint  a tinted chip background       (palette Background2: pale in light, deep in dark)
   -text  text on that tint              (palette Foreground2: the tint's contrasting pair)
   Palette hues other than Red/Green/DarkOrange/Yellow/Berry/LightGreen/Marigold ship ONLY
   Background2, Foreground2 and BorderActive — which is why these three steps are the set.
   =========================================================================== */
:root {
    --antik-kind-teach-mark: var(--colorPaletteBlueBorderActive);
    --antik-kind-teach-tint: var(--colorPaletteBlueBackground2);
    --antik-kind-teach-text: var(--colorPaletteBlueForeground2);
    --antik-kind-review-mark: var(--colorPaletteLavenderBorderActive);
    --antik-kind-review-tint: var(--colorPaletteLavenderBackground2);
    --antik-kind-review-text: var(--colorPaletteLavenderForeground2);
    --antik-kind-note-mark: var(--colorPaletteMarigoldBorderActive);
    --antik-kind-note-tint: var(--colorPaletteMarigoldBackground2);
    --antik-kind-note-text: var(--colorPaletteMarigoldForeground2);
    --antik-kind-task-mark: var(--colorPaletteTealBorderActive);
    --antik-kind-task-tint: var(--colorPaletteTealBackground2);
    --antik-kind-task-text: var(--colorPaletteTealForeground2);
}

/* ===========================================================================
   DATA-DRIVEN COLOURS (#418, docs/design.md § Rules 7) — a colour the app does not choose,
   because it comes from the database. RoleEntity.ColorHex is the one there is: a stored hex,
   drawn as TEXT and as a pill edge on the Çalışanlar list and the worker view. A stored hex has
   no dark twin, and the seeded dark ones — #5c2d91 Koordinatör, #107c10 Öğretmen, #1915ed
   Öğrenci — are 1.5 to 2.7:1 on Koyu's #292929.

   Use: put the stored hex inline as --role-color on an element with class="antik-role-color",
   and read var(--role-ink) wherever it meets text or a border, on that element or inside it.
     Açık  --role-ink IS the stored hex, so nothing changes.
     Koyu  the hex mixed 40% into the theme's primary text colour (white), which keeps the hue
           and lifts it to text contrast.
   MEASURED with the seeded roles on /okullar/calisanlar in Koyu, the pill text on its own tinted pill:
   Koordinatör 5.44:1, Öğretmen 6.13:1, the darkest seeded hex (#1915ed) 5.02:1.

   The dark rule is allowed because no token can lighten a value only the database knows. The
   custom property is declared on the element that carries --role-color, not on :root: a var()
   in a custom property resolves where it is declared, and :root has no --role-color.
   =========================================================================== */
.antik-role-color {
    --role-ink: var(--role-color);
}

body[data-theme="dark"] .antik-role-color {
    --role-ink: color-mix(in srgb, var(--role-color) 40%, var(--colorNeutralForeground1));
}

/* ===========================================================================
   AVATAR SHAPE TOKEN — every avatar in the app is a rounded square (a
   squircle / app-icon shape), not a full circle, driven by ONE custom
   property so the radius never has to be hunted down and changed file by
   file again.

   Why 8px: this app's avatars run 28-48px on a side. At that range, 8px is
   17%-29% of the side (8/48 = 16.7%, 8/28 = 28.6%) — unmistakably a
   soft-cornered square, not a barely-rounded rectangle (which would read at
   a few percent of the side, not double digits).

   Deliberately NOT touched: the nav rail's school logo (100px,
   border-radius: 10px, in Components/Layout/NavMenu.razor.css — that file is
   off limits for this change). At 100px, 8px (this token) and 10px (its own
   hardcoded value) read as the same visual treatment, so leaving that one
   rule un-unified does not clash with this app-wide token.
   =========================================================================== */
:root {
    --avatar-radius: 8px;
}

/* FluentAvatar (<fluent-avatar>) is a web component, and rc.4's shadow-root
   stylesheet genuinely ships a square shape for it, keyed off the `shape`
   attribute Razor puts on the light-DOM host when you set
   Shape="AvatarShape.Square":
       :host { border-radius: var(--borderRadiusCircular); }
       :host([shape="square"]) { border-radius: var(--borderRadiusMedium); }
       :host([shape="square"][size="20"]), :host([shape="square"][size="24"])
         { border-radius: var(--borderRadiusSmall); }
       :host([shape="square"][size="56"]), :host([shape="square"][size="64"]),
         :host([shape="square"][size="72"]) { border-radius: var(--borderRadiusLarge); }
       :host([shape="square"][size="96"]), :host([shape="square"][size="120"]),
         :host([shape="square"][size="128"]) { border-radius: var(--borderRadiusXLarge); }
   So Shape="AvatarShape.Square" does square the host by itself — we are using
   a real library feature here, not fighting a hardcoded circle.

   That built-in square radius is a SIZE-KEYED SCALE, though, not one flat
   value: 2px (--borderRadiusSmall) at 20-24px, 4px (--borderRadiusMedium) at
   the unkeyed default, 6px (--borderRadiusLarge) at 56-72px, 8px
   (--borderRadiusXLarge) at 96-128px. Our one house token (--avatar-radius:
   8px above) deliberately flattens that whole scale to a single value, so a
   32px header avatar and a 40px student avatar read as the exact same shape
   instead of drifting apart by size the way rc.4's own scale would have them
   do.

   The one thing that scale does NOT cover is the slotted image itself:
       ::slotted(img) { box-sizing: border-box; width: 100%; height: 100%;
         border-radius: var(--borderRadiusCircular); }
   has no `[shape="square"]` variant at all — it stays circular
   unconditionally, no matter what the host's `shape` attribute says. That is
   exactly why the second rule below (`fluent-avatar[shape="square"] img`) is
   genuinely required and not redundant with the first: without it, the host
   frame would go square while the photo inside kept painting as a circle
   inside that square frame.

   Both rules below are document-level (light-DOM) selectors against that same
   host element and the `<img>` the caller slots into it as an ordinary child.
   That's why they take effect with no !important needed: a page-authored rule
   that targets a shadow host beats that host's own internal `:host` rules, so
   our two plain selectors override `:host([shape="square"])` and
   `::slotted(img)` above without having to win a specificity fight. */
fluent-avatar[shape="square"] { border-radius: var(--avatar-radius); }
fluent-avatar[shape="square"] img { border-radius: var(--avatar-radius); }

/* ===========================================================================
   SHELL GRID — full-height nav on the left, header inside the content column.

   rc.4's default is  "header header header" / "nav content aside" /
   "footer footer footer", i.e. the header spans the whole page and the nav
   starts under it. v4 does the opposite (v4 MainLayout.razor:5-28: a horizontal
   FluentStack whose first child is the full-height NavMenu, with MainHeader
   stacked above the body inside the second column), so this restores v4's shape.

   ⚠️ This override CANNOT live in MainLayout.razor.css. The grid <div> is
   rendered by the FluentLayout *component*, so it never receives MainLayout's
   b-xxxxxxxxxx CSS-isolation scope attribute, and ::deep has no page-authored
   ancestor to hang off (DESIGN-GUIDELINES.md, "Reason 2 — the scope attribute is
   never on the Fluent element"). It has to be global CSS or an inline Style=.

   ⚠️ It also needs BOTH the `div` prefix and the media query, and neither is
   cosmetic (both established by browser measurement, diary 0040):

   1. `div.` — rc.4's own `.fluent-layout { grid-template-areas: "header header
      header" … }` is (0,1,0), exactly the same specificity as a bare
      `.fluent-layout` here, and it WINS the tie on source order: the package's
      bundle.scp.css is pulled in by an `@import` on the FIRST LINE of the
      generated AntikCocuk.V5.styles.css, and App.razor links that file AFTER
      app.css. So every global override of a FluentUI component's own CSS must
      clear (0,1,0). `.fluent-layout` is always a <div> (FluentLayout's
      BuildRenderTree opens one literally), so `div.` is a free +1.

   2. The media query — because raising specificity above (0,1,0) would ALSO beat
      rc.4's mobile branch. FluentLayout emits an inline <style> whose container
      query resets grid-template-areas to "header"/"content"/"footer" below
      MobileBreakdownWidth, and that rule is also (0,1,0). Scoping this override
      to >=769px hands the mobile case straight back to rc.4 untouched, instead of
      re-implementing it. 769 is therefore coupled to FluentLayout's
      MobileBreakdownWidth (default 768) — change one, change the other. The
      container it queries is a plain div spanning the viewport with no scrollbar
      (default-fuib.css pins `body { height: 100dvh; overflow: hidden }`), so
      container width == viewport width; verified identical behaviour at 768 and
      769 in the browser.
   =========================================================================== */
@media (min-width: 769px) {
    div.fluent-layout {
        grid-template-areas:
            "nav header aside"
            "nav content aside"
            "nav footer aside";
        grid-template-columns: auto 1fr auto;
        grid-template-rows: auto 1fr auto;
    }
}

/* The hamburger lives inside the header band, which in this app is white
   (MainHeader.razor.css) rather than rc.4's brand blue. rc.4 paints the icon
   `fill: var(--colorNeutralForegroundOnBrand)` (white) on .fluent-layout-hamburger,
   AND hands the inner FluentButton `Color="var(--colorNeutralForegroundOnBrand)"`,
   which FluentButton.StyleValue writes out as an INLINE `color:` on the
   <fluent-button>. White on white, twice.

   Re-pointing the TOKEN on the hamburger element fixes both halves at once and is
   why there is no !important here: custom properties inherit, so the inline
   `color: var(--colorNeutralForegroundOnBrand)` on the descendant <fluent-button>
   resolves against this declaration too. (Overriding `fill`/`color` directly would
   have fixed only the half that is not inline.) Measured: the icon computes
   rgb(36,36,36).

   Global, not MainHeader.razor.css, for the same reason as the grid above: every
   element here is rendered by a FluentUI component and carries no b- scope
   attribute. `div.` for the same cascade reason (rc.4's own rules are @imported
   later — see above); the two hamburger rules below are (0,3,0) in rc.4 too. */
div.fluent-layout-item[area="header"] .fluent-layout-hamburger {
    --colorNeutralForegroundOnBrand: var(--colorNeutralForeground1);
}

/* rc.4 gives the hamburger `:hover { background-color: var(--colorBrandBackgroundPressed) }`
   — a dark blue block, correct on its brand-blue header, wrong on a white one. */
div.fluent-layout-item[area="header"] .fluent-layout-hamburger:hover {
    background-color: var(--colorNeutralBackground1Hover);
    border-radius: 4px;
}

/* HEADER PROFILE MENU — keep the popover on screen.
   rc.5's fluent-popover-b places its dialog in adjustDialogPosition() with
   `$>=P ? H() : L>=P ? ee() : H()`, where $ is the space LEFT of the anchor and H()
   aligns the dialog's left edge to the anchor's left edge. It never checks the space to
   the RIGHT, so an anchor at the far right of the header with >= 330px to its left gets
   a left-aligned dialog that runs off the viewport (read from the shipped
   5.0.0-rc.5-26219.1 lib.module.js). The trigger is the right-most thing in the bar, so
   pin the dialog to the viewport's right edge instead. !important is required: the
   script writes `left`/`right` as inline styles on every reposition. `::part(dialog)`
   is the part name the element itself sets on its shadow `div[popover]`. */
#header-profile-popover::part(dialog) {
    left: auto !important;
    right: var(--spacingHorizontalM, 12px) !important;
    max-width: calc(100vw - 2 * var(--spacingHorizontalM, 12px));
}

h1:focus {
    outline: none;
}

.content {
    padding: 1.5rem;
}

.field-row {
    margin-bottom: 1rem;
    max-width: 320px;
}

/* GAG timeline cursor-following tooltip (rendered into document.body by
   js/gag-timeline.js, so it lives outside GagTimeline's component-scoped CSS -
   a JS-created element appended to <body> never receives Blazor's CSS
   isolation scope attribute). Ported from v4's app.css (diary 0026).

   An INVERTED surface, like FluentUI's own inverted tooltip: dark on the light
   theme and light on the dark one, so it always stands out from the page under
   it (#418). It used to be a fixed near-black (#0f172a) with pastel text chosen
   against that black, i.e. the same in both themes. The status colours use the
   *Inverted token family, which exists for text on exactly this surface; the
   muted line is a mix of the surface's own two colours, so it stays between them
   in either theme. */
.gag-tip {
    position: fixed;
    z-index: 50;
    pointer-events: none;
    background: var(--colorNeutralBackgroundInverted);
    color: var(--colorNeutralForegroundInverted);
    border-radius: 8px;
    padding: 8px 10px;
    font-size: 12px;
    line-height: 1.45;
    max-width: 260px;
    box-shadow: var(--shadow16);
}

.gag-tip b {
    color: var(--colorNeutralForegroundInverted);
}

.gag-tip .t-muted {
    /* theme-role: muted text, the tooltip's own ink faded 30% into its inverted surface */
    color: color-mix(in srgb, var(--colorNeutralForegroundInverted) 70%, var(--colorNeutralBackgroundInverted));
}

.gag-tip .t-done {
    color: var(--colorStatusSuccessForegroundInverted);
}

.gag-tip .t-missed {
    color: var(--colorStatusDangerForegroundInverted);
}

.gag-tip .t-upcoming {
    color: var(--colorBrandForegroundInverted);
}

/* ===========================================================================
   DATA GRID FULL WIDTH — opt-in contract: the grid's BOX fills its container,
   and it KEEPS filling it after the user drags a column.

   Opt in per grid:  <CocukDataGrid … Class="data-grid-fullwidth" />
   Grids that do not carry the class are untouched — this whole block is inert
   for them.

   ⚠️ Two independent problems, one contract. The CSS below fixes the BOX; only
   js/datagrid-fullwidth.js fixes the TRACKS. Neither is sufficient alone.

   1. The cascade (why `table.` and not a bare class). rc.4 ships
      `.fluent-data-grid { … width: auto; flex: 1; … }` — specificity (0,1,0),
      extracted from the package's
      Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css (5.0.0-rc.4-26180.1).
      There is NO `table.`-prefixed selector anywhere in that bundle, so (0,1,0)
      is the whole bar. But a TIE LOSES HERE: the bundle is pulled in by an
      `@import` on the FIRST LINE of the generated AntikCocuk.V5.styles.css, and
      App.razor links that file (line 13) AFTER app.css (line 12) — so the
      library's rules land after ours and win any equal-specificity contest on
      source order. We must EXCEED (0,1,0), not match it. `table.` is the free
      +1: the grid element is always a <table> (FluentDataGrid.razor opens one
      literally), making this selector (0,1,1).

      This is the same class of problem as `div.fluent-layout` at the top of
      this file, solved the same way and for the same reason — see the SHELL
      GRID banner's trap 1 for the full source-order argument.

      ⚠️ Mercury's copy of this comment says its rule wins "on the tie, because
      app.css loads AFTER the library bundle". That is true THERE and FALSE
      HERE — our link order is reversed. Do not copy that sentence back in.

   2. Why the CSS alone is not enough (MEASURED at 1440x900 on /anket-gruplari,
      GridTemplateColumns="2fr 1fr 1fr 1fr 400px"). With the AUTHORED template
      the table box is 1142px in a 1142px container and the resolved tracks sum
      to 1142px — gap 0. After reproducing rc.4's post-drag write (an all-pixel
      inline `grid-template-columns`) the table box is STILL 1142px, but the
      tracks now sum to 942px: 200px of dead space on the right, because no
      flexible track remains to reabsorb the slack. THE BOX WAS NEVER THE
      PROBLEM; THE TRACKS ARE. rc.4 writes that all-px template from three
      places in FluentDataGrid.razor.js — the column-drag `moveHandler` (which
      converts EVERY `minmax(...)` token to a pixel value), `ResizeColumnDiscrete`
      / `ResizeColumnExact`, and `AutoFitGridColumns`. It writes it INLINE on the
      element, which no stylesheet rule can outrank at any specificity, so the
      repair cannot be CSS: js/datagrid-fullwidth.js observes that inline write
      and puts one flexible track back. Read its header for the mechanism.

   Template requirements for a grid that opts in:
     • The authored `GridTemplateColumns` MUST contain at least one flexible
       (`fr`) track. That track is what the JS remembers and restores; a fully
       fixed template has nothing to reabsorb the slack and will strand dead
       space exactly as measured above.
     • `AutoFit="true"` is INCOMPATIBLE. `AutoFitGridColumns` produces an
       all-pixel template by design and additionally strips the `auto-fit`
       class, so the grid fights this contract permanently.
     • Do NOT supply column-level `Width=` together with `GridTemplateColumns`:
       rc.4 THROWS. (Verified in FluentDataGrid.razor.cs — `ArgumentException`,
       "You can use either the 'GridTemplateColumns' parameter on the grid or
       the 'Width' property at the column level, not both.")

   Scope note — this contract owns WIDTH ONLY, and still does. Row height is a
   separate app-wide rule; see the GRID ROW HEIGHT banner below. Opting a grid
   into full width here never changes its row heights, so the class stays safe to
   add anywhere. (Mercury scopes its own row spacing to this class, in its own
   app.css:98 — `.data-grid-fullwidth td, th { min-height: 44px }`. That SHAPE
   was deliberately not copied: ours has to reach EVERY grid, including the ones
   without this class, because it replaces a constructor-level default that did.)

   Note: 25 of this app's 33 grids already carry an inline `Style="width: 100%"`.
   Inline beats any stylesheet, so their BOX is already correct today; the class
   replaces that scattered inline with one named contract and additionally covers
   the 8 grids that lack it.
   =========================================================================== */
table.data-grid-fullwidth {
    width: 100%;
}

/* ===========================================================================
   GRID ROW HEIGHT — 58px data rows, 40px headers, app-wide. THIS RULE IS THIS
   APP'S ROW HEIGHT. It is not a workaround and it has no removal condition.

   Where it came from: the height used to be `RowSize = DataGridRowSize.Large`
   in CocukDataGrid's constructor. rc.4 turns `RowSize` into an INLINE `height`
   on the cell — `FluentDataGridCell<TGridItem>.StyleValue` adds
   `height: {(int)Grid.RowSize}px` with NO CellType/RowType guard — and
   `BuildRenderTree` hands that same StyleValue to the <th> as to the <td>. So
   asking for 58px rows also produced 58px HEADERS. That was issue #329.

   Why a stylesheet rule reaches an inline height, with no `!important`:
   IT DOESN'T FIGHT IT, IT CLAMPS IT. `min-height` and `height` are different
   properties, so there is no cascade contest, no specificity problem and no
   source-order problem. Used height is clamp(min-height, height, max-height),
   so this `min-height: 58px` wins over the inline `height: 32px` (rc.4's own
   default RowSize, Small) outright.

   Why the HEADER stays out of it, for free: rc.4 also writes an INLINE
   `min-height: 40px` on every cell whose row type is not Default — i.e. on
   `header` and `sticky-header` rows and nothing else. Inline DOES beat a
   stylesheet on the same property, so the header keeps its own 40px floor and
   this rule cannot reach it even if the selector were widened to `th`. That
   asymmetry is the mechanism this issue is built on, not an accident.
   MEASURED at 1440x900 as a Kurum Müdürü, th/td, before → after:
     /okullar             58/58 → 40/58
     /ogrenci             58/58 → 40/58
     /okullar/calisanlar  58/58 → 40/58

   `:not([multiline="true"])` — the three MultiLine grids are NOT governed by this.
   `MultiLine="true"` makes the grid replace the RowSize height with an inline
   `height: 100%` and tag the DATA cells (only) as multiline, so those rows
   are content-sized on purpose: a two-line "Görev" cell must be allowed to grow
   and a one-line one must be allowed not to. Without the `:not()` this rule
   would out-specify their own floors — `table.fluent-data-grid td` is (0,1,2)
   against the (0,1,1) of a page-level `.fluent-data-grid td` — and quietly
   resize all three. MEASURED at 1440x900 on rc.4, unchanged across this change:
   /sms-gecmisi td 60px (its page-level floor), /gorevler/ata td 49px on a short
   row and 99px on a two-line one.

   ⚠️ rc.5 renamed the tag, SILENTLY. rc.4 put a `multiline-text` CLASS on the
   cell (`FluentDataGridCell.ClassValue`); rc.5 puts a `multiline="true"`
   ATTRIBUTE on it instead (`FluentDataGridCell.BuildAttributes`, also used for
   the plain <td> cells rc.5 renders without a component; MEASURED in the prerendered
   /sms-gecmisi HTML: `<td … role="gridcell" multiline="true">`) and the shipped bundle
   now reads `.fluent-data-grid td[multiline="true"]`; `multiline-text` occurs 0
   times in rc.5's bundle.scp.css and assembly. Left as `:not(.multiline-text)`,
   this rule kept compiling and matched EVERY cell again, putting the 58px floor
   back on the three MultiLine grids. Re-check on a bump with
     grep -o 'td\[multiline[^{]*' <pkg>/staticwebassets/*.bundle.scp.css
   An attribute selector weighs the same as a class, so the specificity above is
   unchanged.

   Scope is app-wide and NOT keyed on `data-grid-fullwidth`: this replaces a
   CONSTRUCTOR-level default that applied to every grid, so scoping it to a
   class would silently shrink every grid that does not carry it. `table.`
   prefix per the house convention in this file — the library's own grid rules
   land AFTER app.css in source order (see the DATA GRID FULL WIDTH banner), so
   a tie loses here. Nothing in the library bundle sets `min-height` on `td`, so
   nothing competes with this today; the prefix is insurance, not a fix.

   Changing the number changes every list in the app. That is the point — it is
   one number in one place. Do NOT re-add `RowSize` to CocukDataGrid to change
   it; that is what put the header at 58px.
   =========================================================================== */
table.fluent-data-grid td:not([multiline="true"]) {
    min-height: 58px;
}

/* ===========================================================================
   GRID EMPTY / LOADING ROW — breathing room for the two "there is nothing to
   show yet" rows, app-wide.

   rc.4 renders both of them itself, as a full-width cell inside the grid's
   <tbody>, and styles them with exactly one rule (verbatim from the package's
   Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css, 5.0.0-rc.4-26180.1):

       .fluent-data-grid .empty-content-cell,
       .fluent-data-grid .loading-content-cell
           { font-weight: 600; text-align: center; height: 100%; user-select: none }

   No padding at all. MEASURED at 1440x900 on /ogrenci's archived grid before
   this rule: the empty cell was 1142 x 21 — a bold sliver hard against the
   header row, which is a large part of why "this page shows nothing when there
   is no data" was a believable bug report.

   Why it is HERE and not in the components' scoped stylesheets: this <td> is
   emitted by FluentDataGrid, so it carries no b-xxxxxxxxxx scope attribute and
   no .razor.css can reach it (DESIGN-GUIDELINES.md, "the scope attribute is
   never on the Fluent element"). A global rule is the only reachable place.

   24px here + 24px inside GridEmptyState / GridLoadingState = the 48px the
   hand-rolled empty-state cards used, and a text-only default (a grid with no
   <EmptyContent>, showing the localizer's "Kayıt bulunmuyor.") still clears the
   header instead of hugging it.

   Specificity note: the library's rule is (0,2,0) and lands AFTER app.css in
   source order (App.razor links AntikCocuk.V5.styles.css, which @imports the
   bundle, after app.css) — so a tie loses here. `table.` makes this (0,2,1) and
   wins outright. Same argument as the DATA GRID FULL WIDTH block above; do not
   "simplify" the selector.

   ⚠️ rc.5 (5.0.0-rc.5-26219.1) dropped both classes — 0 occurrences of
   `empty-content-cell` / `loading-content-cell` in its bundle.scp.css and
   assembly — and marks the ROW instead: `FluentDataGridRow` renders
   `row-state="empty-content" | "loading-content" | "error-content"`
   (`DataGridRowState`, decompiled; MEASURED in prerendered HTML — /okullar
   `row-state="loading-content"`, /sms-gecmisi `row-state="empty-content"`), and the
   bundle's rule became
       .fluent-data-grid tr[row-state="empty-content"] > td, …loading-content…,
       …error-content… { font-weight: 600; text-align: center; height: 100%; … }
   The error row is listed too because rc.4 gave it the same `empty-content-cell`
   class, so this rule always covered it. The padding COMPETITOR is the library's
   `.fluent-data-grid td:not([col-select="true"]) { padding: 0 18px }`, (0,2,1):
   the selector below is (0,2,3) and beats it outright. INFERRED from the
   selectors, not browser-measured: rc.4's version of this rule was (0,2,1) against
   rc.4's identically weighted `td:not(.col-select)` and so may have LOST on source
   order — if an empty grid now looks taller than it did on rc.4, that is this
   padding applying for the first time, not a new defect.
   =========================================================================== */
table.fluent-data-grid tr[row-state="empty-content"] > td,
table.fluent-data-grid tr[row-state="loading-content"] > td,
table.fluent-data-grid tr[row-state="error-content"] > td {
    padding-block: 24px;
}

/* ===========================================================================
   FLUENTTEXT HEADINGS — `<FluentText As="TextTag.Hn">` keeps its Size/Weight.

   rc.4's `As` was INERT: it only wrote an `as="h2"` attribute that nothing read
   (MEASURED, PAGE-PATTERNS.md / #307). rc.5 made it real: `FluentText.BuildRenderTree`
   now wraps ChildContent in an `AddTag` component, i.e. it renders
       <fluent-text size="500" weight="semibold"><h2>Görevler</h2></fluent-text>
   (decompiled from 5.0.0-rc.5-26219.1; `As` also became `TextTag?`, default null — and
   MEASURED in the running app's prerendered /gorevler/ata:
   `<fluent-text size="500" weight="semibold"><h2>Görevler`).
   ~50 call sites in this app pass `As=`.

   That inner <h2> is a light-DOM element, so default-fuib.css's heading rules reach
   it — `h1…h6 { font-weight: var(--fontWeightMedium) }`, `h2 { font-size:
   var(--fontSizeHero800); line-height: var(--lineHeightHero800) }` and so on (the
   same rules DESIGN-GUIDELINES "Every page must render an <h1>" measured growing
   PageCard's title). <fluent-text>'s shadow sheet does say
   `::slotted(*) { font: inherit; line-height: inherit; margin: 0 }`, but for
   normal declarations a document rule beats a `::slotted()` rule from the shadow
   tree (CSS Cascade, "context" step) — the same step the AVATAR rules near the top
   of this file rely on, where a document `fluent-avatar[shape="square"] img` beats
   the avatar's `::slotted(img)` radius. So the Size and Weight parameters would stop
   applying: a Size400 section heading would render at the Hero800 size.
   INFERRED from the shipped CSS and that precedent, not browser-measured for text.

   This puts rc.4's rendering back while keeping rc.5's real heading element:
   everything is inherited from the host, which is where Size/Weight land, and
   `display: inline` keeps the text flowing exactly as the bare text node did (a
   block <h2> inside a non-Block, inline host would break the line). (0,0,2) beats
   default-fuib's (0,0,1) on specificity, whatever the sheet order.
   Removal condition: default-fuib.css stops styling bare h1…h6, or <fluent-text>
   stops wrapping ChildContent in a heading element.
   =========================================================================== */
fluent-text > :is(h1, h2, h3, h4, h5, h6) {
    font: inherit;
    margin: 0;
    display: inline;
}

/* ===========================================================================
   VISUALLY HIDDEN — the standard screen-reader-only utility. NOT a FluentUI
   override: it touches nothing the library ships and is not keyed off any
   fluent-* element. It is the plain, decades-old clip recipe.

   What it is for: an element that must exist for assistive tech and for
   Routes.razor:25's <FocusOnNavigate Selector="h1" />, but that must not
   appear on screen. Today that is the <h1> on the two activation pages
   (WorkerActivationPage / ParentActivationPage): those are login-style pages
   with no visible page title, and inventing one would be new on-screen copy —
   a design change nobody asked for.

   Why it lives HERE and not in a .razor.css: it rides on plain authored HTML,
   so a scoped rule WOULD reach it (the b-xxxxx scope attribute lands on plain
   HTML, unlike a Class= passed to a Blazor component) — but two different
   pages need the same rule, and duplicating it is how the two copies drift.

   Why this recipe and not something shorter: `display: none` and
   `visibility: hidden` remove the element from the accessibility tree as well
   as from the screen, which defeats the entire purpose — a hidden <h1> that
   screen readers cannot see is just a missing <h1>. Clipping a 1x1 box keeps
   the element rendered and announceable. `clip` is the legacy form and
   `clip-path` the modern one; both are kept because they cost nothing and the
   pair is what every audited implementation of this utility ships.
   `white-space: nowrap` stops a long string from being wrapped into a tall
   column inside the 1px box, which some screen readers read one word per line.
   =========================================================================== */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ===========================================================================
   #blazor-error-ui — the framework's own "the circuit died" bar.

   MOVED HERE from Components/Layout/MainLayout.razor.css (issue #408 §1.2),
   together with the element itself, which now lives in App.razor.

   Why it cannot stay scoped: a scoped rule compiles to `#blazor-error-ui[b-xxxxx]`
   and matches only elements carrying MainLayout's scope attribute. With the
   element hoisted to App.razor — so that the 7 EmptyLayout pages get an error
   bar at all — the `display: none` below would have matched nothing and the bar
   would have been visible on every page, permanently. Rules and element move
   together or not at all.

   blazor.web.js finds the element with document.querySelector('#blazor-error-ui')
   at error time and flips its display, so a single static copy in App.razor
   serves every layout and every render mode.

   Colours (#418): the warning recipe FluentMessageBar uses — a status Background1
   surface with its Border1 edge — instead of the template's fixed `lightyellow`,
   which glared in the dark theme. Tokens are safe here: the bar can only appear
   after blazor.web.js has started, by which time the library's sheet is adopted,
   and a dead circuit does not remove it.
   =========================================================================== */
#blazor-error-ui {
    background: var(--colorStatusWarningBackground1);
    color: var(--colorNeutralForeground1);
    border-top: 1px solid var(--colorStatusWarningBorder1);
    bottom: 0;
    box-shadow: var(--shadow4);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

/* ===========================================================================
   PASTE-LIMIT NOTICE (#408)

   Shown by wwwroot/js/paste-limit-notice.js when a paste is clipped by a field's
   maxlength. Every free-text input carries one (Utils/TextLimits.cs) so that an
   oversized value cannot blow past the 32 KB SignalR message limit and kill the
   circuit — but the browser clips silently, and silent truncation of a consent
   text is worse than a visible refusal.

   Purely client-side: no circuit traffic, no component, and it must keep working
   on the static-SSR login page, which never starts a circuit at all.

   Colours (#418): tokens, in the same warning recipe as #blazor-error-ui above.
   They used to be hard-coded on the belief that the token sheet is missing on the
   static-SSR login page. It is not: blazor.web.js starts on that page too and runs
   FluentUI's initializer, which adopts the theme sheet in beforeWebStart —
   MEASURED on /hesap/giris, see docs/design.md — and a paste can only happen after
   the page has loaded.
   =========================================================================== */
#antik-paste-limit-notice {
    background: var(--colorStatusWarningBackground1);
    border: 1px solid var(--colorStatusWarningBorder1);
    border-radius: 4px;
    bottom: 16px;
    box-shadow: var(--shadow8);
    box-sizing: border-box;
    color: var(--colorNeutralForeground1);
    font-size: 13px;
    left: 50%;
    line-height: 18px;
    max-width: min(520px, calc(100vw - 32px));
    opacity: 0;
    padding: 10px 14px;
    pointer-events: none;
    position: fixed;
    transform: translateX(-50%) translateY(8px);
    transition: opacity 0.15s ease, transform 0.15s ease;
    z-index: 1001;
}

    #antik-paste-limit-notice.is-visible {
        opacity: 1;
        transform: translateX(-50%) translateY(0);
    }
