/* Modal -- design-handoffs/asportal-public/design-guide/Portal_Modal.css.
   Border/shadow/radius/backdrop values match admin's modal.css exactly.
   Implemented on <dialog> for native focus-trapping/Esc-to-close/backdrop
   behavior, same as admin.

   One real difference from admin: the only size this guide shows
   ("01. Medium (기본)") is 560px wide -- that's admin's *secondary*
   `.modal-medium` variant, not its 416px default `dialog.modal` base
   width. Since Portal has no 416px example in this guide at all, 560px
   is the base `dialog.modal` width here, not an opt-in modifier class --
   revisit if a narrower Portal modal ever turns up in a real screen. */

dialog.modal {
  box-sizing: border-box;
  /* Explicit, not just inherited from html/body (typography.css) -- a
     native <dialog> renders in the browser's "top layer" and WebKit
     doesn't reliably cascade font-family into it from its DOM ancestors,
     confirmed via getComputedStyle on real iPhone Safari. Belt-and-
     braces alongside the html/body fix, since this element is the one
     actually sitting in the top layer. */
  font-family: var(--font-family);
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  width: 560px;
  max-width: calc(100vw - 48px);
  /* Caps the dialog itself to the actual viewport height (same "shrink to
     fit what's really there" reasoning as max-width above) -- .modal-body
     below is the one part that scrolls internally when content doesn't
     fit, header/footer stay fixed/visible. overflow:hidden here (not
     visible) is what makes that clip happen at the dialog's own edge
     instead of content silently spilling out past it with no way to
     reach it -- confirmed as a real bug, not a hypothetical one, on
     #trainingModal (public-spec 7, 5 sections) at a short viewport: the
     footer/submit button rendered ~400px below the dialog's own reported
     bottom edge, invisible and unreachable, no scrollbar anywhere.

     100dvh (dynamic viewport height) overrides the 100vh fallback where
     supported -- on real mobile Safari specifically, 100vh is the
     *larger* toolbar-collapsed height, not whatever's actually visible
     right now with the address bar showing. A dialog capped against
     that inflated value can render taller than the true visible area
     with no way to reach the rest (no scrollbar triggers, since nothing
     "overflows" the miscalculated max-height) -- reported live as every
     popup showing with no readable height on a real iPhone. Same fix
     already applied to .login-bg (login.css). */
  max-height: calc(100vh - 48px);
  max-height: calc(100dvh - 48px);
  overflow: hidden;
  /* Fade in/out -- allow-discrete is what makes a <dialog>'s own show/
     hide (display:none <-> flex, plus top-layer registration) genuinely
     transitionable at all instead of an instant snap with nothing for
     `opacity` to interpolate across; @starting-style below supplies the
     "before" frame OPEN needs (showModal() makes [open] true
     synchronously, so without it the very first paint already has
     opacity:1). CLOSE needs separate JS handling regardless --
     confirmed on real WebKit that allow-discrete alone animates OPEN
     but not CLOSE -- see static/js/popup.js's window.animatedCloseDialog
     and the .closing rule below, same mechanism navigation.css's
     .sidebar already uses for its own slide. `::backdrop` is
     deliberately NOT included in this fade (stays instant, as before) --
     animating it was tried for the nav drawer first and broke mobile
     Safari's own bottom-toolbar tint (it samples the backdrop's color
     while still mid-fade/near-transparent and never picks up the real
     dark value), and dialog.modal's own ::backdrop below covers the
     same full viewport by the same UA default, so the same risk
     applies here even though it wasn't independently re-confirmed
     broken for this specific component.

     opacity: 0 is the BASE/default value here (not 1) -- [open] below
     is what raises it to 1 while genuinely open. This has to exactly
     mirror whatever `.closing` (further down) forces, or closing gets a
     real, confirmed (via Chromium, not just WebKit) "flashes twice" bug:
     with a mismatched base of opacity:1, the instant the real .close()
     removes the [open] attribute, the applicable rule for opacity snaps
     from `.closing`'s 0 back to base's 1 -- and since that same close()
     call is also what changes display/overlay (both allow-discrete),
     the browser keeps the box rendered long enough for that snap-back
     to actually paint as a visible phantom re-fade-in, even though the
     dialog is already meant to be gone. Matching the base value to
     `.closing`'s means [open]'s removal is a 0-to-0 no-op for opacity --
     nothing left to (mis)animate. .sidebar (navigation.css) never had
     this bug for exactly this reason: its own base/closed transform
     already matches what its own .closing forces. */
  opacity: 0;
  transition:
    opacity 0.15s ease,
    display 0.15s allow-discrete,
    overlay 0.15s allow-discrete;
}

dialog.modal[open] {
  opacity: 1;
}

@starting-style {
  dialog.modal[open] {
    opacity: 0;
  }
}

/* nav-drawer.js/popup.js's shared close-animation pattern (see the
   comment above) -- window.animatedCloseDialog adds this class and
   waits for the transition to finish before the real .close(). Matches
   the base rule's own opacity:0 exactly, per that rule's own comment. */
dialog.modal.closing {
  opacity: 0;
}

/* Pairs with the dialog's own autofocus+tabindex="-1" (see #trainingModal/
   #passwordModal's own template comment) -- focus lands here specifically
   so the header's X button doesn't silently steal it instead, but a
   default browser focus outline around the *entire card* looks like a
   rendering glitch, not an intentional focus indicator (there's no
   "next" focus target being highlighted -- the whole modal just
   appeared). */
dialog.modal:focus {
  outline: none;
}

/* display:flex has to be scoped to [open] specifically, not on the bare
   dialog.modal selector above -- the native UA stylesheet's own
   `dialog:not([open]) { display: none }` has the exact same specificity
   (0,1,1) as `dialog.modal`, and a same-specificity later rule (this
   stylesheet loads after the UA one) wins regardless of the [open]
   state, so an unconditional `display:flex` here silently kept every
   modal visible even while closed -- caught immediately after this same
   change (max-height/overflow above) shipped, from a real "closing the
   training popup leaves it on screen" report. */
dialog.modal[open] {
  display: flex;
  flex-direction: column;
}

dialog.modal::backdrop {
  background: rgba(27, 27, 27, 0.6);
}

/* #passwordModal/#trainingModal wrap .modal-header/.modal-body/.modal-footer
   in two separate <form>s (one method="dialog" for the X button, one
   method="post" for the real submit) -- these become real nested flex
   items/containers below instead of the flex layout above only ever
   seeing two form-shaped blobs. #resultModal has no such wrapper (its
   .modal-body/.modal-footer are direct dialog.modal children already)
   and is unaffected either way.

   (This block used to be a single `dialog.modal > form { display:
   contents }` rule. Replacing it with real nested containers here was a
   reasonable cleanup on its own -- display:contents has genuine Safari
   bugs -- but it was NOT what actually fixed the real bug below; that
   turned out to be flex-basis, not display:contents. Left as real
   containers anyway since there's no reason to go back.) */
dialog.modal > form:first-of-type {
  flex: none;
}

dialog.modal > form:last-of-type {
  /* 1 1 auto, not the bare `1` shorthand (which is 1 1 0%) -- root-caused
     live on a real iPhone via Safari's own remote Web Inspector
     (getBoundingClientRect on the actual broken page): .modal-body
     rendered at height:0 while .modal-footer, right next to it, rendered
     at its correct 84px and simply overflowed past the dialog's own
     collapsed box. dialog.modal has no explicit height of its own (only
     max-height, a cap) -- its real height is meant to come from its
     content, like a plain block element. A flex-basis of 0% tells a
     flex item "start from nothing, then grow to fill whatever space the
     container hands you", which is fine when the container's own size
     is externally given, but here the container's size is *supposed to
     come from the children* -- a circular sizing problem browsers
     resolve inconsistently, and WebKit collapsed straight to 0 in
     exactly this "auto-height flex column, flex:1 children" shape.
     flex-basis:auto instead starts from the content's own natural size
     first, only shrinking (flex-shrink still 1) once max-height
     genuinely forces it to -- confirmed fixed by live-patching this
     exact rule via Web Inspector before touching this file: .modal-body
     went from height 0 to 147, footer landed back inside the dialog's
     own box instead of overflowing past it. */
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.modal-header {
  flex: none;
  display: flex;
  align-items: center;
  padding: 0 24px;
  gap: 8px;
  height: 56px;
}

/* H3 (18/700/150%) at every breakpoint -- Portal_Font.css's H3 doesn't
   shrink on mobile, so only the dialog's own width changes below, not
   this title's size. */
.modal-title {
  flex: 1;
  margin: 0;
  font-family: var(--font-family);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-text-primary);
}

.modal-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 8px;
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  cursor: pointer;
  color: var(--color-text-muted);
}

.modal-close:active {
  background: var(--color-surface-muted);
}

@media (hover: hover) {
  .modal-close:hover {
    background: var(--color-surface-muted);
  }
}

.modal-body {
  /* 1 1 auto, not the bare `1` shorthand -- see dialog.modal > form:
     last-of-type's own comment above for the full root-cause writeup
     (confirmed live on a real iPhone via Web Inspector: this rule at
     flex-basis:0% is what collapsed .modal-body to height:0). */
  flex: 1 1 auto;
  /* Lets this shrink below its content's natural height instead of
     forcing the flex container (and the dialog itself) to grow past
     max-height to fit it -- a flex item's default min-height is `auto`
     (effectively its content size), which would otherwise defeat the
     max-height/overflow:hidden on dialog.modal above entirely. */
  min-height: 0;
  overflow-y: auto;
  padding: 0 24px;
}

/* Result notices are deliberately compact (Portal alert-modal guide): unlike
   data-entry dialogs, every result popup has a 380px maximum content frame.
   The base viewport cap still takes precedence on a narrow phone. */
#resultModal {
  width: 380px;
}

/* Preserve intentional line breaks in result-popup copy without changing
   whitespace behavior for the forms hosted by other modal bodies. */
#resultModal-body p {
  /* Match Admin's shared result modal: each message is a plain consecutive
     Body 1 line, not a browser-default paragraph with 1em margins between
     entries. */
  margin: 0;
  white-space: pre-line;
  color: var(--color-text-muted);
}

.modal-footer {
  flex: none;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 24px 24px 24px;
}

/* Mobile dialogs use all available horizontal space between the guide's
   16px side gutters. Header, radius, and side-by-side footer layout remain
   unchanged. */
@media (max-width: 767px) {
  dialog.modal {
    width: calc(100vw - 32px);
    max-width: calc(100vw - 32px);
  }
}

/* 비밀번호 변경 popup (profile/index.html, public-spec section 6.1) --
   `Portal_내 정보 > 비밀번호 변경.css` (a real Figma CSS export) shows this
   modal's own .form-table using a narrower label column than a full
   page's .form-table (160px, not the page-level 180px) -- same "modal-
   embedded variant" pattern admin's own #passwordModal already
   established, confirmed independently against this export rather than
   assumed from admin's numbers. Scoped to #passwordModal specifically,
   not every .modal -- #trainingModal below has its own separately-
   reviewed rules instead, since a shared .modal-level rule would assume
   every Portal modal's label column matches without actually checking. */
#passwordModal .form-row {
  padding: 0;
}

#passwordModal .form-label {
  box-sizing: border-box;
  width: 160px;
  padding: 8px;
}

#passwordModal .form-value {
  box-sizing: border-box;
  padding: 8px;
}

#passwordModal .form-row:first-of-type {
  border-top: 1px solid var(--color-border-strong);
}

#passwordModal .form-row:last-of-type {
  border-bottom: 1px solid var(--color-border-strong);
}

/* 12px gap from the form-table to the hint below it (the export's own
   "List wrap" gap), and the hint's own smaller/muted styling -- both
   distinct from forms.css's page-level .form-hint (6px margin, 13px
   font), confirmed via the same export. */
#passwordModal .form-hint {
  margin-top: 12px;
  font-size: 14px;
  color: var(--color-text-muted);
}

/* Mobile export (`M) Portal_내 정보 > 비밀번호 변경.css`) shows the SAME
   inline row layout as PC/Tablet (label left, input right) -- not a
   stacked one, confirmed by its own `flex-direction: row` throughout --
   just a narrower 120px label column (not 160px) so the shrunk 328px-wide
   dialog (base rule above) still leaves the input a legible 144px, not
   the ~104px it'd be squeezed to at the page-level 160px width. First
   attempt here mistook the PNG's own tall single-row spacing for a
   stacked layout and added an unneeded column-direction override --
   caught by cross-checking the actual CSS export before shipping it. */
@media (max-width: 767px) {
  #passwordModal .form-label {
    width: 120px;
    font-size: 14px;
  }
}

/* 교육 신청 popup (public-spec section 7) -- `Portal_교육 신청.css` shows the
   same modal-embedded 160px label column as #passwordModal (confirmed
   independently in this export too, not assumed), for its 교육 신청자/교육
   정보 sections. Unlike #passwordModal's single bare-.form-row block, this
   popup has two genuinely separate bordered sections (신청자/정보), so each
   uses its own real .form-table wrapper -- already gets its own top/bottom
   border for free from that class's own base rule, no first-of-type/
   last-of-type override needed here (교육 과정/희망 교육 일정 use
   .option-list instead, base/forms.css, which has no label column). */
#trainingModal .form-row {
  padding: 0;
}

#trainingModal .form-label {
  box-sizing: border-box;
  width: 160px;
  padding: 8px;
}

#trainingModal .form-value {
  box-sizing: border-box;
  padding: 8px;
  /* Caught via getBoundingClientRect/scrollWidth on Mobile, not visible at
     a glance: an unbroken token with no natural break point (an email
     address, no spaces) can exceed this popup's own value column -- 328px
     dialog minus a 120px label leaves far less room than the page-level
     내 정보 screen's own 100px-label/full-viewport-width layout, where the
     same long email fits fine (confirmed directly, no overflow there).
     Scoped to this modal specifically rather than the shared .form-value,
     since nothing else has shown this gap yet. */
  overflow-wrap: anywhere;
}

@media (max-width: 767px) {
  #trainingModal .form-label {
    width: 120px;
    font-size: 14px;
  }
}
