/*
  Setiry's palette, taken from the marketing site so the two are one brand.

  Three colours carry it. The navy is the ground the mark sits on and the dark theme's
  background. The blue is what a button is — solid, with white on it. The cyan is the accent:
  the mark's axes and bars, a selection, and what the blue becomes on a dark ground, where
  #0016D6 is too dark to read against the navy.

  The greys are Tailwind's slate, which is what the marketing site uses and what
  data-base-color says on <html>.
*/
:root {
    --setiry-navy: #0F1828;
    --setiry-blue: #0016D6;
    --setiry-cyan: #00B3E6;
}

/*
  Blueprint's primary, pointed at Setiry's own.

  Defined here rather than by data-primary-color, because none of the colours Blueprint
  ships is this one, and Program.cs sets DefaultPrimaryColor to Default so that attribute is
  never written.

  The selectors carry [data-base-color] for a reason that costs an afternoon to find
  otherwise: the base-color blocks set --primary as well as the greys, and
  `.dark[data-base-color="slate"]` outranks a bare `.dark`. Matching their specificity and
  coming later in the document is what makes these win. The attribute is always there —
  index.html writes it and ThemeService keeps it.
*/
:root[data-base-color] {
    --primary: var(--setiry-blue);
    --primary-foreground: #ffffff;
    --ring: var(--setiry-blue);
}

/*
  On a dark ground the solid blue is unreadable — it is nearly the background — so the accent
  takes over, which is what the marketing site does on its own dark sections. Navy on cyan,
  not white: white on #00B3E6 is about 2.4:1 and fails at any size.
*/
.dark[data-base-color] {
    --primary: var(--setiry-cyan);
    --primary-foreground: var(--setiry-navy);
    --ring: var(--setiry-cyan);
}

::selection {
    background: var(--setiry-cyan);
    color: var(--setiry-navy);
}

/*
  The handful of layout utilities the /app pages use that the design system does not ship.

  BlazorBlueprint's stylesheet is precompiled: it contains the Tailwind utilities its own
  components need and nothing else, and there is no build step here to generate more. A
  class it happens not to use simply does not exist, and the failure is silent — mb-6 was
  on every page header in this app and did nothing at all.

  Defined by hand rather than by adding a Tailwind build, which would be a lot of pipeline
  for seven rules. Values match Tailwind's scale so the markup stays readable next to the
  classes that do come from the design system.
*/

/*
  Blueprint v4'ten itibaren blazorblueprint.css içindeki her utility "bb:" önekiyle gelir
  (.bb\:flex, .bb\:sm\:grid-cols-2). Önek almamış bir "flex" artık hiçbir kurala denk gelmez,
  bu yüzden işaretlemedeki kütüphane sınıfları "bb:flex" olarak yazılır. Bu dosyadakiler
  uygulamanın kendi sınıfları olduğu için öneksiz kalır.

  ml-2, ml-auto, text-left ve text-right v3'te kütüphanenin derlenmiş CSS'inde vardı; v4'te
  bileşenler bunları kullanmadığından hiçbir biçimde yok. Sessizce kaybolmasınlar diye burada.
*/
.ml-2 { margin-left: 0.5rem; }
.ml-auto { margin-left: auto; }
.text-left { text-align: left; }
.text-right { text-align: right; }

.mb-6 { margin-bottom: 1.5rem; }
.mb-8 { margin-bottom: 2rem; }
.h-14 { height: 3.5rem; }
.no-underline { text-decoration: none; }

.last\:border-0:last-child { border-width: 0; }

/* Server and port side by side once there is room for them. */
@media (min-width: 640px) {
    .sm\:grid-cols-server-port { grid-template-columns: 1fr 9rem; }
}

@media (min-width: 1280px) {
    .xl\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/*
  A fixed height, not a viewport-relative one.

  The grid virtualizes, and a virtualizer works out its window from the scroll container.
  Sized in vh it renders zero rows wherever the viewport measures as zero — with no error,
  which is how the data-table spike lost an hour.
*/
/*
  Where a report's rows are shown.

  Both axes scroll, and the box never grows past the column it sits in. It used to say
  overflow-y only, so a report with more columns than the window is wide pushed the whole
  page sideways — the header, the canvas and the sidebar with it — and the way back was the
  browser's own horizontal scrollbar.

  min-width: 0 is what makes that stick. The shell lays its content out with flex, and a flex
  item's floor is the width of its contents unless it is told otherwise, so max-width alone
  loses to a wide table.
*/
.report-grid-host {
    height: 560px;
    max-width: 100%;
    min-width: 0;
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius, 0.5rem);
}

/*
  And the table inside is as wide as it needs to be, rather than squeezed into the box.

  Without this the columns compress until every cell wraps — a row of identifiers becomes
  four lines tall and still does not fit — which is a worse way to lose than scrolling.
*/
.report-grid-host table {
    width: max-content;
    min-width: 100%;
}

.report-grid-host th,
.report-grid-host td {
    white-space: nowrap;
}

/* The design system ships max-w-md and max-w-2xl but none of these. */
.max-w-xl { max-width: 36rem; }
/* Wide enough for a paragraph of prose to stay near sixty-five characters. */
.max-w-3xl { max-width: 48rem; }
.max-w-xs { max-width: 20rem; }

/*
  The account pages: one card in the middle of the page, no shell around it.
  Written here rather than as utilities because it is a layout, not a set of tweaks.
*/
.account-page {
    min-height: 100vh;
    display: flex;

    /* A column since the language switch joined the card above it. Centred as before, which
       for a column means the cross axis — so the card keeps its own width. */
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
    background: var(--background);
}

.account-card {
    width: 100%;
    max-width: 26rem;
    padding: 2rem;
    border: 1px solid var(--border);
    border-radius: var(--radius, 0.5rem);
    background: var(--card, var(--background));
}

.account-brand {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1.5rem;
    text-decoration: none;
    color: var(--foreground);
}

/*
  The plot behind the account screens.

  It sits inside .account-page rather than being fixed to the viewport, so that on a short
  window — a phone in landscape, a form that has grown an error and two alerts — it covers the
  page rather than one screen's worth of it and leaves a bare strip below.

  The ink is the brand's own, written as rgba rather than through the theme tokens on purpose:
  --primary is a solid colour meant to carry white text on it, and a backdrop needs the same
  hue at a twentieth of that weight. Cyan on the dark ground and blue on the light one, which
  is the same swap the buttons make and for the same reason — #0016D6 disappears into the navy.
*/
.account-page {
    position: relative;

    /* The card and the language switch are positioned above the plot; this keeps that stacking
       to this page instead of letting it interact with anything the app draws over it. */
    isolation: isolate;
    overflow: hidden;
}

.account-backdrop {
    position: absolute;
    inset: 0;
    z-index: 0;

    /* Decorative. A click meant for the page must not land on the drawing. */
    pointer-events: none;

    --plot-ink: var(--setiry-blue);
    --plot-rule: rgba(0, 22, 214, 0.075);
    --plot-axis: rgba(0, 22, 214, 0.12);
    --plot-bar: rgba(0, 22, 214, 0.055);
    --plot-series: rgba(0, 22, 214, 0.24);
    --plot-ghost: rgba(0, 22, 214, 0.10);
    --plot-wash-a: rgba(0, 179, 230, 0.07);
    --plot-wash-b: rgba(0, 22, 214, 0.07);
}

/*
  On the navy the blue is nearly the background, so the accent takes over — the same trade
  .dark[data-base-color] makes for --primary a few hundred lines up.
*/
.dark .account-backdrop {
    --plot-ink: var(--setiry-cyan);
    --plot-rule: rgba(0, 179, 230, 0.10);
    --plot-axis: rgba(0, 179, 230, 0.20);
    --plot-bar: rgba(0, 179, 230, 0.08);
    --plot-series: rgba(0, 179, 230, 0.34);
    --plot-ghost: rgba(0, 179, 230, 0.14);
    --plot-wash-a: rgba(0, 179, 230, 0.11);
    --plot-wash-b: rgba(0, 22, 214, 0.15);
}

/* The wash: two soft fields drifting against each other, which is what stops a flat colour
   from reading as flat. Inset past the edges so the drift never brings an edge into view. */
.account-backdrop::before {
    content: "";
    position: absolute;
    inset: -25%;
    background:
        radial-gradient(38% 42% at 24% 26%, var(--plot-wash-a), transparent 70%),
        radial-gradient(46% 40% at 80% 76%, var(--plot-wash-b), transparent 72%);
    animation: plot-wash 30s ease-in-out infinite alternate;
}

/* A lift under the card, so the form keeps its own contrast whatever the plot is doing behind
   it. Wide and soft: the point is that nobody sees an edge, only that the middle is calmer. */
.account-backdrop::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(32rem 28rem at 50% 50%, var(--background), transparent 100%);
    opacity: 0.75;
}

.account-plot {
    width: 100%;
    height: 100%;
    display: block;
    fill: none;
}

/*
  Drawing on, left to right.

  --len is the line's own length in user units, measured in the component from the points it
  is drawn from. The dash is that long, so one dash covers the whole line and pushing the
  offset out by --len hides it exactly.
*/
.plot-rule,
.plot-axis,
.plot-base,
.plot-ghost,
.plot-series {
    stroke-dashoffset: 0;
    animation: plot-draw 1400ms cubic-bezier(0.33, 0.9, 0.35, 1) both var(--wait, 0ms);
}

@keyframes plot-draw {
    from { stroke-dashoffset: var(--len); }
}

/* The rules and the two axes are straight, so their length is the span they cover and the
   component does not need to work it out. */
/*
  No vector-effect on any of these, and it is not an oversight.

  non-scaling-stroke was here for a while to keep the rules a hairline at every window size,
  and it silently broke the drawing-on: with it, the browser measures the dash pattern in
  screen pixels while --len counts user units, so on a window where the plot is scaled up half
  again the dash runs out early and every rule stops three-quarters of the way across. It
  looked exactly like a line that had been drawn wrong rather than a dash that was too short.
  The stroke scaling with the drawing is the lesser cost, and at these widths invisible.
*/
.plot-rule,
.plot-base {
    --len: 1100;
    stroke: var(--plot-rule);
    stroke-width: 0.8;
    stroke-dasharray: 1100;
}

/* Same geometry as a grid rule, one step darker: it is the axis, not the paper. */
.plot-base {
    stroke: var(--plot-axis);
    stroke-width: 1;
}

.plot-axis {
    --len: 540;
    stroke: var(--plot-axis);
    stroke-width: 1;
    stroke-dasharray: 540;
}

.plot-series {
    stroke: var(--plot-series);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: var(--len);
    animation-duration: 2600ms;
    animation-delay: 500ms;
}

.plot-ghost {
    stroke: var(--plot-ghost);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: var(--len);
    animation-duration: 2600ms;
    animation-delay: 300ms;
}

/*
  The bars: up once, then a breath each.

  Two animations on one property, and the order matters — the sway is declared second so it
  wins once it starts, and it is delayed until the rise has finished. It begins at scaleY(1),
  which is exactly where the rise left off, so the handover is invisible.
*/
.plot-bar {
    fill: var(--plot-bar);
    transform-box: fill-box;
    transform-origin: bottom;
    animation:
        plot-rise 900ms cubic-bezier(0.22, 0.85, 0.3, 1) both var(--wait, 0ms),
        plot-sway var(--sway, 9s) ease-in-out infinite var(--sway-wait, 1s);
}

@keyframes plot-rise {
    from { transform: scaleY(0); }
}

@keyframes plot-sway {
    0%, 100% { transform: scaleY(1); }
    50% { transform: scaleY(var(--dip, 0.9)); }
}

/*
  The projection. A fixed dash rather than an animated one — this line is dotted because of
  what it means, so its dash pattern is not available to draw it on with, and it arrives by
  fading in once the solid line it grows out of has finished.
*/
.plot-forecast {
    stroke: var(--plot-series);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-dasharray: 1 14;
    opacity: 0;
    animation: plot-appear 1200ms ease-out both 2900ms;
}

@keyframes plot-appear {
    from { opacity: 0; }
    to { opacity: 0.55; }
}

/* Where the line has reached. */
.plot-tip {
    fill: var(--plot-ink);
    opacity: 0;
    animation: plot-arrive 700ms ease-out both 2700ms;
}

.plot-halo {
    fill: var(--plot-ink);
    transform-box: fill-box;
    transform-origin: center;
    opacity: 0;
    animation: plot-pulse 3400ms ease-out infinite 2900ms;
}

@keyframes plot-arrive {
    from { opacity: 0; transform: scale(0.4); }
    to { opacity: 0.55; transform: scale(1); }
}

@keyframes plot-pulse {
    0% { opacity: 0.22; transform: scale(0.5); }
    70% { opacity: 0; transform: scale(1.9); }
    100% { opacity: 0; transform: scale(1.9); }
}

@keyframes plot-wash {
    from { transform: translate3d(-1.5%, 1%, 0) scale(1); }
    to { transform: translate3d(1.5%, -1%, 0) scale(1.06); }
}

/*
  Everything the plot does is ambient — nothing is waiting on it and nothing is announced by
  it — so for a reader who has asked for less motion it simply arrives finished. The base
  values are the resting ones and the keyframes hold the starting state, which is why turning
  the animations off leaves a drawn chart rather than a blank rectangle.
*/
@media (prefers-reduced-motion: reduce) {
    .account-backdrop *,
    .account-backdrop::before,
    .account-backdrop::after {
        animation: none;
    }

    /* These two are the only things whose resting state is not their base value: both start
       invisible and are brought in by the animation that is no longer running. The halo is
       left out on purpose — it is nothing but a pulse, and a pulse held still is a blob. */
    .plot-tip { opacity: 0.55; }
    .plot-forecast { opacity: 0.55; }
}

/* Above the plot. Without this the card is in the same stacking context at the same level and
   the drawing wins on document order. */
.account-card,
.account-language {
    position: relative;
    z-index: 1;
}

/* The card used to sit on a flat field where nothing could pass behind it. Now something can,
   so it needs to look like it is in front rather than printed on. */
.account-card {
    box-shadow:
        0 24px 60px -24px rgba(15, 24, 40, 0.45),
        0 4px 12px -6px rgba(15, 24, 40, 0.2);
}

.dark .account-card {
    box-shadow:
        0 28px 70px -24px rgba(0, 0, 0, 0.65),
        0 4px 12px -6px rgba(0, 0, 0, 0.4);
}

/*
  The advanced designer: tools on the left, canvas in the middle, settings on the right.

  Fixed heights rather than viewport-relative ones, for the same reason the report grid has
  them: a canvas told it has no height draws nothing, silently.
*/
/*
    The advanced designer: a palette, a canvas and an inspector.

    Narrow first, because the three-column version did not merely overflow on a phone — it
    resolved to "200px 2px 280px". The two asides kept their fixed widths and the canvas, being
    the 1fr, collapsed to nothing: a designer with a palette, an inspector and no working area
    at all. minmax(0, 1fr) below is what stops that happening again at any width, because a
    bare 1fr will not shrink past its content.
*/
.designer {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem;
}

/* Room to arrange something in, once the canvas has the width to itself. */
.designer-canvas { min-height: 24rem; }

.designer-tool-list { display: grid; gap: 0.25rem; }

/*
    Side by side once there is room for all three and a canvas worth having: two asides,
    two gaps and 320px to work in.
*/
@media (min-width: 900px) {
    .designer {
        grid-template-columns: 200px minmax(0, 1fr) 280px;
        height: 520px;
    }

    .designer-canvas { min-height: 0; }

    /* Filling the aside is what an entry in a vertical list wants. It used to live in the
       button reset below, where it also applied to the row and made every chip a screen
       wide — the reset is about undoing browser styling, not about layout. */
    button.designer-tool { width: 100%; }
}

.designer-tools,
.designer-inspector {
    overflow-y: auto;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius, 0.5rem);
    background: var(--card, var(--background));
}

.designer-canvas {
    position: relative;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius, 0.5rem);
    background: var(--muted, #f8fafc);
}

.designer-tool {
    display: grid;
    grid-template-columns: 1.5rem 1fr;
    gap: 0 0.5rem;
    padding: 0.4rem 0.5rem;
    margin-bottom: 0.25rem;
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    cursor: grab;
    background: var(--background);
}

.designer-tool:hover { border-color: var(--primary); }
.designer-tool-glyph { grid-row: span 2; font-size: 1rem; align-self: center; }
.designer-tool-name { font-size: 0.8rem; font-weight: 500; }
.designer-tool-summary { font-size: 0.7rem; color: var(--muted-foreground); }

/*
    Below that, one column — and the palette lies down.

    A vertical list of eleven tools would push the canvas off the bottom of a phone before
    anybody saw it, so it becomes a row that scrolls sideways: the tools are a thing you pick
    from, not a thing you read. Tapping one adds it to the canvas, which is what the palette
    already did for touch, since HTML5 dragging does not exist there.
*/
@media (max-width: 899px) {
    .designer-tools,
    .designer-inspector {
        overflow-y: visible;
    }

    .designer-tool-list {
        display: flex;
        gap: 0.5rem;
        overflow-x: auto;
        padding-bottom: 0.25rem;
    }

    button.designer-tool {
        /* max-content, not auto: a grid's auto track fills its container, so the chips came
           out one screen wide each and the row showed a single tool at a time. */
        flex: 0 0 auto;
        width: max-content;
        grid-template-columns: 1.25rem max-content;
        margin-bottom: 0;
        white-space: nowrap;
    }

    /* The one-line description is for a list somebody is reading down, not a row. */
    .designer-tool-summary { display: none; }
}

/* A tool on the canvas. The fabric.js version composed this out of shapes. */
.tool-node {
    position: relative;
    min-width: 8.5rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    background: var(--background);
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.06);
}

.tool-node.selected { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary); }
.tool-node.source { border-left: 3px solid var(--primary); }
.tool-glyph { font-size: 0.95rem; }
.tool-title { font-size: 0.8rem; font-weight: 500; }
.tool-subtitle { font-size: 0.7rem; color: var(--muted-foreground); }

.tool-port {
    position: absolute;
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    background: var(--primary);
    top: 50%;
}

.tool-port.left { left: -0.3rem; transform: translateY(-50%); }
.tool-port.right { right: -0.3rem; transform: translateY(-50%); }

/* The palette entries are buttons now, so the browser's own button styling has to go. */
button.designer-tool {
    text-align: left;
    font: inherit;
    color: inherit;
}

/* Prose for the setup guides. Blueprint's stylesheet only carries the utilities its own
   components use, so a page of plain <p>, <h2>, <ul> and <pre> has nothing to style it. */
/*
    The guide pages' own typography.

    Colours are var(--x), not hsl(var(--x)). The design system's variables hold complete
    colours — oklch(...) — rather than the bare HSL components the older convention assumed,
    so wrapping them produced hsl(oklch(...)), which is not a colour. Nothing errored: the
    declarations were simply dropped, and every code block, border and note on these pages
    rendered unstyled for as long as they had existed.
*/
.guide { line-height: 1.65; }
.guide > * + * { margin-top: 1rem; }
.guide h2 {
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin-top: 2rem;
}
.guide ul { padding-left: 1.25rem; list-style: disc; }
.guide li + li { margin-top: 0.35rem; }
.guide a { color: var(--primary); }
.guide code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.875em;
    background: var(--muted);
    border-radius: 0.25rem;
    padding: 0.1em 0.35em;
}
.guide pre {
    background: var(--muted);
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    padding: 0.85rem 1rem;
    overflow-x: auto;
}
.guide pre code { background: none; padding: 0; font-size: 0.8125rem; }
/* A note is an aside, not a step: it should read as quieter than what surrounds it.

   The policy page has one too — the line saying it is published in English — so the rule
   covers both rather than the class silently doing nothing on one of them. */
.guide .note,
.policy .note {
    border-left: 2px solid var(--border);
    padding-left: 0.9rem;
    color: var(--muted-foreground);
    font-size: 0.9rem;
}

/*
  The pages anyone can reach without signing in: the privacy policy, the error page, the one
  that says a door is closed. Wider than the account card, which is sized for a form.
*/
.public-page {
    min-height: 100vh;
    background: var(--background);
}

.public-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.25rem 1.5rem;
    border-bottom: 1px solid var(--border);
}

.public-header .account-brand { margin-bottom: 0; }

.public-main { padding: 2.5rem 1.5rem 4rem; max-width: 52rem; margin: 0 auto; }

/*
  The privacy policy. It is a numbered legal document, so the numbers are part of the content
  and sit in their own column rather than being faked with a list marker.
*/
.policy { line-height: 1.6; font-size: 0.9375rem; }
.policy h1 { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.02em; }
.policy h2 {
    font-size: 1rem;
    font-weight: 600;
    margin-top: 2rem;
    margin-bottom: 0.75rem;
}
.policy .clause { display: flex; gap: 0.75rem; margin-bottom: 0.6rem; }
.policy .clause-number {
    flex: 0 0 2.75rem;
    color: var(--muted-foreground);
    font-variant-numeric: tabular-nums;
}
.policy .clause p { margin: 0; }
.policy .clause-list {
    list-style: lower-alpha;
    margin: 0 0 0.9rem 3.5rem;
    padding: 0;
}
.policy .clause-list li { margin-bottom: 0.4rem; padding-left: 0.35rem; }

/*
  What is on screen before the app is.

  These have to be in a plain stylesheet rather than anything the design system provides,
  because they are drawn while the runtime is still downloading and nothing Blazor ships has
  loaded yet. Colours are literals for the same reason: the theme tokens come from the
  component library's stylesheet, which is a separate download and may not have arrived.
*/
.boot {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  background: #0F1828;
  color: #f8fafc;
}

.boot-mark { font-size: 1.25rem; font-weight: 600; letter-spacing: -0.01em; }
.boot-note { font-size: 0.875rem; color: #94a3b8; }

/*
  Blazor's own error bar. Hidden until the runtime shows it, which it does by setting
  display: block from JavaScript — so the rule that hides it has to be here rather than
  inline, or there would be a red band across every page from the first paint.
*/
#blazor-error-ui {
  display: none;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  padding: 0.75rem 1.25rem;
  background: #fef3c7;
  color: #422006;
  box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
}

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

/*
  The glyph on a sidebar item.

  Not a <span>, and that is the whole point of the class. The design system hides every direct
  span child of a menu button when the rail is collapsed — it expects the icon to be an <svg>
  and everything else to be the label — so a glyph in a span disappeared along with the words
  and the collapsed sidebar showed nothing at all.

  <i> without the italics, then, and it does not shrink: the button is a fixed square when
  collapsed, and a glyph that can shrink is a glyph that ends up half a pixel wide.
*/
.nav-glyph {
  font-style: normal;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  line-height: 1;
}

/* A link or a snippet of markup, shown to be copied. Long, unbroken, and not to be edited. */
.break-all { word-break: break-all; }
.block { display: block; }
.rounded-md { border-radius: calc(var(--radius, 0.5rem) - 2px); }

/* The fields a shared report asks for, above its rows. */
.shared-parameters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem;
}

.shared-parameter-input {
    display: block;
    margin-top: 0.25rem;
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    background: var(--background);
    color: var(--foreground);
    font-size: 0.875rem;
}

.shared-parameter-input:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 1px;
}

/*
  A dashboard: reports side by side.

  Written as real rules rather than as Tailwind utilities because the utilities for a
  three-column grid with per-item spans are not in the precompiled stylesheet this app
  ships — and a class that does not exist lays nothing out while looking exactly right in
  the markup. The grid is what a dashboard is, so it is not a place to find that out in a
  browser.

  Three columns, because a tile is read rather than laid out and every extra width is
  another arrangement to get wrong. One column below the same breakpoint the rest of the
  app stacks at: two reports side by side on a phone are two reports nobody can read.
*/
.dashboard-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    align-items: start;
}

@media (min-width: 1024px) {
    .dashboard-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    /* The span is data rather than a class per width: it comes from the row. */
    .dashboard-tile[data-width="2"] { grid-column: span 2; }
    .dashboard-tile[data-width="3"] { grid-column: span 3; }
}

.dashboard-tile {
    min-width: 0;
}

/*
  The rows inside a tile.

  Its own box rather than .report-grid-host: that one is 560px tall because it holds a whole
  report, and a tile is a glance. This one grows to what it holds and stops, so a tile showing
  three rows is three rows tall and one showing a hundred does not push the rest of the
  dashboard off the screen.
*/
.dashboard-tile-rows {
    max-height: 22rem;
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius, 0.5rem);
}

.dashboard-tile-rows table {
    width: max-content;
    min-width: 100%;
}

.dashboard-tile-rows th,
.dashboard-tile-rows td {
    white-space: nowrap;
    padding: 0.4rem 0.7rem;
    font-size: 0.8125rem;
    text-align: left;
}

.dashboard-tile-rows th {
    position: sticky;
    top: 0;
    background: var(--card, var(--background));
    font-weight: 500;
    border-bottom: 1px solid var(--border);
}

.dashboard-tile-rows tbody tr + tr td {
    border-top: 1px solid var(--border);
}

/*
  Several form fields on one line.

  Aligned by their tops, not their bottoms. Bottom alignment looks right until one field has
  helper text under it and its neighbour does not: the taller one's label and box both ride
  up, and a row of controls that should read as one line reads as two. Every label here is
  one line, so aligning the tops aligns the boxes and leaves the helper text hanging below
  where it belongs.
*/
.form-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.75rem;
}

/* A field that takes the space the fixed-width ones leave. */
.form-row .grows {
    flex: 1 1 14rem;
    min-width: 12rem;
}

/*
  Anything that is not a field — a button, a row of them — still lines up with the boxes
  rather than with the labels above them.
*/
.form-row .with-the-fields {
    padding-top: 1.6rem;
}

/* One row of the tile editor, in a box of its own so the tiles read as a list. */
.dashboard-tile-editor {
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius, 0.5rem);
}

/*
  An alert that is going off.

  A left edge rather than a filled card: the list is read at a glance and often on a phone,
  and what has to carry is "which of these is the one" — a colour running down the side does
  that at any size, where a tinted background stops working the moment the screen is dim.
*/
.alert-firing {
    border-left: 3px solid var(--destructive, #dc2626);
}

/* One alert's facts, laid out so the numbers line up rather than wrapping mid-sentence. */
.alert-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1.25rem;
    font-size: 0.8125rem;
    color: var(--muted-foreground);
}

.alert-facts b {
    font-weight: 500;
    color: var(--foreground);
}

/*
  Renaming a column, in the tick list.

  Small and quiet: it is there for the person who wants "Kaydı ekleyen" instead of
  created_by, and invisible to everybody else — the placeholder is the database's own name,
  so an empty box reads as "leave it alone" rather than as something unfilled.
*/
.column-name-input {
    display: block;
    width: 100%;
    margin-top: 0.25rem;
    padding: 0.2rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: 0.25rem;
    background: var(--background);
    color: var(--foreground);
    font-size: 0.75rem;
}

.column-name-input:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 1px;
}

/* One column's row in the tick list: the tickbox beside the name, the rename box under it. */
.column-choice {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    font-size: 0.875rem;
}

.column-choice .grows {
    flex: 1 1 auto;
    min-width: 0;
}

/*
  The panel everything is drawn in.

  A flex item's min-width is auto, which means "never shrink below what is inside me". A
  report with twenty columns then made this panel wider than the window, and every
  max-width: 100% within it measured against the widened panel — so the table that was
  supposed to scroll inside its box stretched the page instead.
*/
.setiry-inset {
    min-width: 0;
}

/*
    The line between two ways of doing the same thing.

    A rule either side of the word rather than a rule with the word laid over it, so there is
    nothing to keep an opaque background on and the divider works on whichever ground the
    theme puts behind it.
*/
.or-else {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 1.25rem 0;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted-foreground);
}

.or-else::before,
.or-else::after {
    content: "";
    flex: 1;
    border-top: 1px solid var(--border);
}

/*
    A value somebody has to copy into somewhere else.

    The label on one side and the value on the other, and the value in a box that can be
    selected in one gesture — these are pasted into a provider's console, and a redirect
    address that is one character short is a sign-in that fails with a message about a
    mismatch.
*/
.sso-fact {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    font-size: 0.8125rem;
}

.sso-fact > span {
    color: var(--muted-foreground);
    min-width: 9rem;
}

.sso-fact > code {
    flex: 1;

    /* Without it a long address widens the card past the page rather than wrapping. */
    min-width: 0;
    overflow-wrap: anywhere;

    padding: 0.15rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: 0.25rem;
    background: var(--muted);
    font-size: 0.75rem;
}

/*
    A heading over a group of guides.

    Quieter than the page's own title and louder than the cards under it, with the space above
    that says the group before it has ended. The first one wants less room, because there is a
    paragraph above it already.
*/
.help-section {
    margin: 2rem 0 0.75rem;
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--muted-foreground);
}

.help-section:first-child {
    margin-top: 0;
}

/*
    A row of questions somebody can click instead of thinking of one.

    Wraps, because the questions are sentences and a sentence is not a chip. The label sits on
    its own line above them for the same reason: putting it inline would leave one question
    orphaned beside it on every width but one.
*/
.suggested-questions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 1.25rem;
}

.suggested-questions > span {
    flex-basis: 100%;
    font-size: 0.8125rem;
    color: var(--muted-foreground);
}

/*
    What a report does, said on its card.

    An aside rather than the card's own description: the description is something a person
    wrote and this is something a model said, and the two should not look alike.
*/
.report-explanation {
    border-left: 2px solid var(--border);
    padding-left: 0.9rem;
    font-size: 0.85rem;
    line-height: 1.55;
    color: var(--muted-foreground);
}


/*
  The query editor's text area.

  Monospaced and tall, because SQL is written in columns and read in lines: a proportional
  face makes an aligned SELECT list stop being aligned, and a three-line box makes anybody
  writing a join scroll while they think. Resizable vertically only — sideways would take it
  out of the card it lives in.
*/
.query-editor {
    width: 100%;
    min-height: 12rem;
    resize: vertical;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--background);
    color: var(--foreground);
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.875rem;
    line-height: 1.6;
    tab-size: 4;
}

.query-editor:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
}

/*
  What the database said, kept as it was written.

  A database's error names a line and a position and lines its caret up under the offending
  character; reflowing it as prose throws all of that away. It scrolls rather than wraps for
  the same reason.
*/
.query-error {
    display: block;
    white-space: pre-wrap;
    overflow-x: auto;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.8125rem;
}

/*
    Asking a database a question, and the answers.

    A transcript rather than a form. What makes it read as a conversation is that the questions
    stay on the screen: the third question is nearly always a change to the second, and a page
    that cleared itself between them would be a page where nobody could see what they had
    changed it from.
*/
.chat {
    display: grid;
    gap: 1rem;
    max-width: 56rem;
}

.chat-turns {
    display: grid;
    gap: 1.25rem;
}

/* The question, on the reader's side. */
.chat-asked {
    justify-self: end;
    max-width: 85%;
    background: var(--muted);
    color: var(--foreground);
    border-radius: 0.75rem 0.75rem 0.125rem 0.75rem;
    padding: 0.55rem 0.9rem;
    font-size: 0.9375rem;
    line-height: 1.5;
    white-space: pre-wrap;
}

/* The answer in words. The largest thing on the card, because it is the answer. */
.chat-answer {
    font-size: 1.0625rem;
    line-height: 1.6;
    margin-bottom: 0.85rem;
}

/*
    What the assistant understood the question to be.

    Quieter than the answer and set apart from it, because it is the part to read when the
    answer looks wrong: the number is only as good as the report behind it.
*/
.chat-understood {
    border-left: 2px solid var(--border);
    padding-left: 0.9rem;
    margin-bottom: 0.85rem;
    font-size: 0.85rem;
    line-height: 1.55;
    color: var(--muted-foreground);
}

/*
    The rows behind the answer, scrolling in their own box.

    Capped in height on purpose. They are evidence, not the point — somebody who wants to
    read them properly has the button that opens the report in the designer.
*/
.chat-rows {
    overflow: auto;
    max-height: 18rem;
    border: 1px solid var(--border);
    border-radius: calc(var(--radius, 0.5rem) - 2px);
}

.chat-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8125rem;
}

.chat-table th,
.chat-table td {
    text-align: left;
    padding: 0.4rem 0.7rem;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}

.chat-table th {
    position: sticky;
    top: 0;
    background: var(--card);
    font-weight: 600;
}

.chat-table tr:last-child td { border-bottom: none; }

.chat-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.85rem;
}

/* What leaves, said where the typing happens rather than in a policy nobody opens. */
.chat-note {
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--muted-foreground);
}

.chat-composer {
    display: grid;
    gap: 0.75rem;
}

/*
    A card that has no header.

    BlazorBlueprint's card content is "p-6 pt-0" — padding on three sides and none on top —
    which is right when a header sits above it and supplies the top itself. Half the cards in
    this app have no header: a form, an editor, a composer. On those the first thing inside
    sits hard against the top border, which reads as two boxes drawn into each other rather
    than one box with something in it.

    Restored only when the content is the card's first child, so a card that does have a header
    is untouched. Written here rather than as a Class on every card, because the next card
    somebody adds would be the one that gets forgotten.

    Blueprint v4 aynı sınıfları "bb:" önekiyle yazıyor (bb:bg-card, bb:p-6 bb:pt-0); seçici
    öneksiz kalsaydı hiçbir karta denk gelmez, başlıksız kartlar yine kenara yapışırdı.
*/
.bb\:bg-card > .bb\:p-6.bb\:pt-0:first-child {
    padding-top: 1.5rem;
}

/*
    The SQL editor's completion list.

    Positioned against the editor rather than the page, so it follows the caret down as a
    query gets longer and does not have to be moved when the card does.
*/
.query-editor-host {
    position: relative;
}

.sql-suggestions {
    position: absolute;
    z-index: 30;
    margin: 0;
    padding: 0.25rem;
    list-style: none;
    min-width: 15rem;
    max-width: 26rem;
    max-height: 14rem;
    overflow-y: auto;
    background: var(--popover, var(--card));
    color: var(--popover-foreground, var(--card-foreground));
    border: 1px solid var(--border);
    border-radius: calc(var(--radius, 0.5rem) - 2px);
    box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
}

.sql-suggestion {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.3rem 0.5rem;
    border-radius: calc(var(--radius, 0.5rem) - 4px);
    cursor: pointer;
    font-size: 0.8125rem;
    line-height: 1.4;
}

.sql-suggestion.chosen,
.sql-suggestion:hover {
    background: var(--accent, var(--muted));
    color: var(--accent-foreground, var(--foreground));
}

/*
    What kind of thing it is, in three letters.

    A column, a table and a keyword are chosen for different reasons, and which one is being
    offered is the difference between pressing Tab and typing another letter. Fixed width so
    the names line up under each other, which is the whole reason a list is scannable.
*/
.sql-suggestion-kind {
    flex: none;
    width: 1.75rem;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--muted-foreground);
}

.sql-suggestion-text {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    white-space: nowrap;
}

/* The table and type behind a column, pushed to the end so it never crowds the name. */
.sql-suggestion-detail {
    margin-left: auto;
    padding-left: 0.75rem;
    font-size: 0.6875rem;
    color: var(--muted-foreground);
    white-space: nowrap;
}

/*
    The language switch.

    A real <select> rather than a menu of buttons, on purpose: it is a two-item choice that
    every browser already knows how to draw, on every platform, with the keyboard and the
    screen reader working without anybody writing that part. The only thing added is the
    globe, which is what makes it recognisable to somebody who cannot read either option.
*/
.language-picker {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.language-globe {
    position: absolute;
    left: 0.5rem;
    width: 1rem;
    height: 1rem;
    color: var(--muted-foreground);

    /* Behind the select, which covers it: without this the icon eats the click that should
       open the list. */
    pointer-events: none;
}

.language-select {
    appearance: none;
    height: 2rem;
    padding: 0 0.55rem 0 1.85rem;
    border: 1px solid transparent;
    border-radius: calc(var(--radius, 0.5rem) - 2px);
    background: transparent;
    color: var(--foreground);
    font-size: 0.8125rem;
    line-height: 1;
    cursor: pointer;
}

.language-select:hover {
    background: var(--accent, var(--muted));
    border-color: var(--border);
}

.language-select:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
}

/* The list itself is the browser's, drawn in the browser's colours — so the options have to
   be told what they are, or a dark page gets a light popup with unreadable text. */
.language-select option {
    background: var(--popover, var(--card));
    color: var(--popover-foreground, var(--foreground));
}

/* The switch above the sign-in card, where somebody who cannot read the form will look. */
.account-language {
    width: 100%;

    /* The card's width, so the switch lines up with its right edge rather than the page's. */
    max-width: 26rem;
    margin-bottom: 0.5rem;
    display: flex;
    justify-content: flex-end;
}

/* What a form refused, under it. Its own list so the messages stack rather than run on. */
.form-problems {
    list-style: none;
    margin: 0.75rem 0 0;
    padding: 0;
    display: grid;
    gap: 0.25rem;
    font-size: 0.875rem;
    color: var(--destructive);
}

/* ----------------------------------------------------------------------------------------
   The engine picker on the connect form, and the marks it draws.

   A grid rather than a row: six engines at the width of a name plus an icon do not fit on a
   phone, and auto-fill lets the browser decide how many go on a line instead of this file
   guessing at breakpoints.
   ---------------------------------------------------------------------------------------- */
.engine-picker {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
    gap: 0.5rem;
}

.engine-option {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.625rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: calc(var(--radius, 0.5rem) - 2px);
    background: transparent;
    cursor: pointer;

    /* The whole tile is the label, so a click anywhere in it reaches the radio inside. */
    user-select: none;
}

.engine-option:hover {
    background: var(--accent, var(--muted));
}

.engine-option[data-selected="true"] {
    border-color: var(--ring);

    /* A second ring drawn just inside the border rather than a thicker border, which would
       move the text by a pixel as the selection changes. */
    box-shadow: inset 0 0 0 1px var(--ring);
    background: var(--accent, var(--muted));
}

/* The radio itself: present for the keyboard and for assistive technology, and never drawn.

   Clipped rather than hidden. display:none and visibility:hidden both take an input out of
   the accessibility tree and off the tab ring, which would leave this control reachable by
   mouse only. */
.engine-option-input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* Focus belongs on the tile, since the input that has it cannot be seen. */
.engine-option:has(.engine-option-input:focus-visible) {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
}

.engine-option-name {
    font-size: 0.875rem;
    line-height: 1.25rem;
}

/* Marks keep their own proportions and never take part in flex shrinking: an elephant
   squeezed to fit a long connection name is worse than a name that wraps. */
.database-mark {
    display: block;
    flex: none;
}
